IntelliJ Elixir - Elixir plugin for JetBrain's IntelliJ Platform

First up: @KronicDeth, thank you so much for your work on the plugin.

I hope this is the right place for this:
Could anyone point me towards a demo video or conference talk with someone just using the plugin so that I can understan what is possible with it?

I’m new to Elixir coming from PHP development in PHPStorm. I’ve loved the productivity increases that PHPStorm enables and I’d like to stay in the JetBrains camp to leverage my existing knowledge of their tools.

The reason I ask for a video is because I’m aware that my expectations of the plugins capabilities may be wrong because I’ve come from an “official” IDE experience in PHPStorm. I’ve searched YouTube and Twitch but I haven’t been able to find anyone just developing using the plugin. One example of something I’m not sure whether it should do something or not is when working with a phoenix controller there doesn’t appear to be any completion for calls to functions like render or redirect, and I’m not sure if there should be. In this example I know that some completion is working because local functions are autocompleted in a really useful way.

I’m aware that there may be all sorts of useful functionality in the elixir plugin that I’m unaware of. I’ve read the comprehensive instructions on the github README, but to be honest I’m not sure I know enough about Elixir to fully understand what the instructions are telling me.

1 Like

Version 11.9.1

Donate (Lowest/Median/Mean/Record is $1/14.19/26.34/200.00 USD.)

Thanks

Changelog

v11.9.1

Bug Fixes

  • #1877 - @KronicDeth
    • Fix syntax highlighting stopping due to decompiling of Kernel failing caused by bugs introduced in #1834.
      Syntax highlighting could fail part way through a file when Kernel needed to be decompiled to resolve parts of the syntax. This would lead to the file to be colored above a certain point, but then the default gray after that point.

      • Connect compiled stubs to decompiled source by name/arity.
        Previously, when the decompiler didn’t use the Docs, it was guaranteed that the PsiCompiled stubs would correlate, in-order, with the decompiled source call definitions, and so mirrors could be set by matching up the list of them in order. Since the Docs doesn’t have to correspond to and doesn’t correspond to the binary precisely for some BEAMs, most importantly, Elixir.Kernel.beam, the PsiCompiled stub and decompiled source is now matched by name and arity. This means some mirrors are missed, but no errors are produced.
      • Allow decompile source to map to entire arity range
        Since the decompile source from Docs can have default arguments the call definition clauses can have an arity range and not just a fixed arity, so all arities in the range need to be mappable to PsiCompiled functions.
      • Use correct macro for signature from Docs.
        Ensures that defmacro is used for macros instead of hard-coding def as in #1834.
      • Use ~S""" for docs from Docs chunk.
        The docs from the Docs chunk may contain interpolation in the code samples or #{ in regex examples, but these should not be treated as an interpolation start as the Docs format does not support interpolation. Anything that looks like interpolation in the docs text was actually escaped in the original docs, so also escape it here by using S""", which turns off interpolation.
      • Log an error if Code function can’t be matched to decompiled source.
        Unlike the old InvalidMirrorException, this won’t be an exception and the binary <-> decompile will still work for the other functions/macros in the file, so it will be a more graceful degradation.
  • 1878 - @KronicDeth
    • Fix missed references to DepsWatcher as project component
      DepsWatcher was converted to a Project Listener in #1844 to support installing the plugin from the Marketplace without reloading, but some references to DepsWatcher were still trying to get its instance for project using project.getComponent(), which would now return null.
  • #1879 - @KronicDeth
    • Target Java 1.8 for JPS compatibility.
      JPS (JetBrains Project System) is the API used to allow External Builders, like mix to build projects.
  • #1880 - @KronicDeth
    • Fix matching unquote(ATOM) call definitions to compiled function by using the argument tounquote when calculating the name of the call definition clause if possible.
      Needed to match anonymous function names that are unquoted because they contain / to separate the outer function name from the anonymous function naming convention.
    • Don’t use Docs signature for MacroNameArity that is an operator or unquoted
      The signatures for operators and unquoted don’t produce valid code that can be parsed.
    • Don’t use signatures for __struct__ functions.
      The signatures for the __struct__ functions are like %Module{}, but that’s not parseable, so bypass the signatures with a specialized SignatureOverride decompiler that matches the actual code in defstruct.
    • Don’t indent empty lines from Docs for @moduledoc and @doc to match the formatter output.
  • #1881 - @KronicDeth
    • Fix capitalization of Docs @moduledoc
      @moduleDoc -> @moduledoc

Installation Instructions

3 Likes

@KronicDeth do you think it likely that you will support wsl running any time soon? The issue has been up a while now. I would be surprised if even 30% of elixir devs on windows remain outside wsl now (number sponsored by pulling it out of my reat, but I think I can’t be far off).

I wanted to give this a swirl again, but windows based elixir dev is pretty lackluster experience.

Version 11.9.2

Donate (Lowest/Median/Mean/Record is $1/14.19/26.34/200.00 USD.)

Thanks

Changelog

v11.9.2

Bug Fixes

  • #1887 - @KronicDeth
    • Protect from nested heredocs in documentation from stopping documentation’s heredoc as happens in Module and Ecto.Query.
      • Use ~S for @moduledoc too, in addition to @doc.
      • Check if the documentation contains """ or ''' and use the opposite one as the promoter/terminator.
        • If neither is used, use """.
        • If both are used, use """, but then escape """ as \"\"\".
      • Trim trailing whitespace from documentation lines to match formatter output.
  • #1890 - @KronicDeth
    • Set runIde maxHeapSize to 7g
      • Set to the same I run my own IDE at, so the debugged instance isn’t any slower than the normal IDE when I need to do extended all day testing to trigger bugs.
    • Test that all FormattingTest files can be parsed.
    • YYINITIAL is special - wrappers of the lexer assume that if in YYINITIAL, it is safe to shift the lexer over when there is an error, having {OPENING_CURLY} pushAndBegin(YYINITIAL) when it was either in YYINITIAL or INTERPOLATION means that the lexer looked like it was restartable when it really wasn’t. This code has been in the lexer for 6 years.
      • When in YYINITIAL, { no longer recurses into YYINITIAL as } does not need to be counted to determine if it is closing an interpolation.
      • When in INTERPOLATION, { enters INTERPOLATION_CURLY to allow counting and matching of } until it can exit and go back to INTERPOLATION, where } will exit the interpolation.
      • When in INTERPOLATION_CURLY, { enters another level of INTERPOLATION_CURLY to allow counting and matching of } until it can exit and go up a level.
    • The } in YYINITIAL did yybegin(ADDITION_OR_SUBTRACTION_MAYBE), but it should have been pushAndBegin(ADDITION_OR_SUBTRACTION) as ADDITION_OR_SUBTRACTION_MAYBE or its following states all exit with handleInLastState() or popAndBegin(). This was not caught in #1859 because the extra YYINITIAL from pushAndBegin(YYINTIAL) hid the bug.
    • Prevent nested YYINITIAL bugs in the future by erroring
      • If trying to pushAndBegin(YYINITIAL).
      • If trying to push(YYINITIAL) and the state stack is not empty
    • Clear the state Stack when ElixirFlexLexer#reset is called, as at the level of typing and pasting, the ElixirFlexLexer is wrapped in many layers of lexers including LexerEditorHighlighter where the ElixirFlexLexer is no longer re-instantiated when there is no text, but instead, ElixirFlexLexer#reset is only called. This has always been an invariant violation since the stack state was added 7 years ago. It likely only became more apparent with the changes to +/- parsing in #1859 that made return-to-YYINITIAL less likely.
      • Since this stack.clear() has to be manually added to ElixirFlexLexer.java, which is generated from Elixir.flex, ResetTest is added to check that the code is still there.
    • For a reason I haven’t been able to explain, the LexerEditorHighlighter stops working after : is typed at the start of an atom in certain situations, such as before ) inside a function call, like when adding an argument. In this case, the old version of the lexer would mark ) as a BAD_CHARACTER and continue to do so until an expected atom start of [a-zA-Z_], ', ", or an operator occurred. Now, if an invalid atom start is matched, the ATOM_START state ends and the previous state handles the text, which in the function case mean ) is parsed as CLOSING_PARENTHESIS. This change allows the highlighting to continue. I do not know if returning BAD_CHARACTER will always break the LexerEditorHighlighter, but I don’t think, so since the GroovyLexer in intellij-community returns it, but it may be the case that it isn’t actually returned ever when users are typing and only handled by the lexer for completeness.

Installation Instructions

2 Likes

I don’t really use my Windows desktops anymore and getting Windows to run on my laptop always run into problems, so I don’t test out Windows all that often. I’ll likely fix asdf on macOS before then since I’m using asdf (as opposed to homebrew) for a client project currently.

I might be doing a revamp of the SDK/Library system in general soon though. I want to not have to run the the SDK to get the version (I likely can get the versiot from the .beam) and someone from the intellij-rust team said there’s a way to do Libraries where I can use the name of the library instead of the ebin, so it is easier to look through External Libraries.

1 Like

Version 11.10.0

Donate (Lowest/Median/Mean/Record is $1/14.19/26.34/200.00 USD.)

Thanks

Changelog

v11.10.0

Enhancements

  • #1893 - @KronicDeth
    • Simplify onlyTemplateDateFileType
  • #1897 - @KronicDeth
    • Add missing opcodes to Code disassembler
      • OTP 23 opcode bs_start_match4
      • Current (in-development) OTP 24 opcodes
        • make_fun3
        • init_yregs
        • recv_marker_bind
        • recv_marker_clear
        • recv_marker_clear
        • recv_marker_user
  • #1899 - @KronicDeth
    • Log PsiElement if Call#finalArguments contain a null.
  • #1902 - @KronicDeth
    • Suggest ASDF directories as homepaths for both Elixir and Erlang for Elixir SDKs.
  • #1821 - @jacekgajek
    • Run ‘Dialyzer based inspections (Elixir)’ using Elixir SDK running mix dialyzer or any customized mix command.

Bug Fixes

  • #1893 - @KronicDeth
    • Use VirtualFile#fileType instead of EEx Type::INSTANCE when looking up extensions.
      Since LEEx file Type is a subclass of EEx’s file Type, it calls templateDataFileTypeSet in EEx’s Type, but templateDataFileTypeSet uses INSTANCE from EEx. By using the VirtualFile#fileType instead, it will properly be EEx or LEEx based on the actual file extension and then it can be used to strip that off and find the DataTemplateLanguage, such as HTMLLanguage for .html.leex.
  • #1897 - @KronicDeth
    • Compare max opcode in to file to max opcode number, not ordinal.
      Opcodes are 1-based, but the ordinal of the Kotlin Enums are 0-based, so the comparison was off-by-1 when a file had the max opcode and would be incorrectly marked as too new.
  • #1899 - @KronicDeth
    • Don’t return null left or right infix operands in primaryArguments
      operation.infix.Normalized.leftOperand and .rightOperand ensures that PsiErrorElement is not returned: they can return null when there is no left or right operand. Infix.primaryArguments was not taking this into account and so could return a null as one of the primaryArguments, which broke Call.finalArguments.
  • #1902 - @KronicDeth
    • Don’t attempt to execute elixir -e "System.version |> IO.puts" to get the version number as it requires too much of a full SDK to be built and from the Erlang paths to be correct too, which was rarely the case for ASDF. Since for Homebrew and ASDF, the directory name is the version name, this shouldn’t be a loss in naming ability. If the directory name is not a parseable version it will be of the format Elixir at <path>. This is probably more correct for installs directories that aren’t versioned as SDK versions aren’t updated if the version installed at the path changes, such as /usr/local/elixir or /opt/elixir, etc.

README updates

Dialyzer

Inspection

Batch Mode
  1. Analyze > Run Inspection by Name… (⌥⇧⌘I)
  2. Type “Dialyzer”
  3. Select “Dialyzer based inspections (Elixir)” from the shortened list
  4. Hit Enter.

You’ll be presented with a “Run ‘Dialyzer based inspections (Elixir)’” dialog

Run 'Dialyzer based inspections (Elixir)'

  1. Change the scope if you want.
  2. Click “OK”

The Inspections Result Tool Pane will open and show results as each file is processed.

  1. Click the :arrow_forward: to expand the Credo section to show all warnings

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

Installation Instructions

2 Likes

Wsl2 support officially out, have you given any thoughts to if you will support it? :slight_smile:

Version 11.11.0

:heart: Sponsor

Historical One-time/Monthly Donations:

Stat Amount
Minimum $1.00
Median $6.25
Mean $12.52
Maximum $200.00

Thanks

Changelog

v11.11.0

Enhancements

  • #1948 - @KronicDeth
    • Reference Resolution

      • Allow any Alias in a chain to have references.
        This allows going to the declaration of Phoenix, Phoenix.LiveView, or Phoenix.LiveView.Socket depending on whether you’re on the Phoenix, LiveView, or Socket Alias, respectively, in the chain.

        • Allow any Alias in Qualified Alias to be resolved
          This allows going to the declaration of Phoenix, Phoenix.LiveView, or Phoenix.LiveView.Socket depending on whether you’re
          on the Phoenix, LiveView, or Socket Alias, respectively, in the chain.

        • Update DocumentationProvider to work with improved Alias resolution

          Without these changes the DocumentationProvider double-resolves and so ends up showing the docs for def and defmodule, instead of the call definition clause or module, respectively.

      • Reimplement Module references
        Instead of references for only the outermost QualifiableAlias, there is a reference for each right-most Alias at a given position, so instead of there only be a reference to App.Context.Schema in App.Context.Schema, there is now a reference to App in the App prefix, a reference to App.Context in Context in App.Context, and a reference to App.Context.Schema in Schema in App.Context.Schema. Not only is this more useful, being able to jump to parent namespaces, but it fixed some of the capability issues with Go To Definition in the 2020 line of IDEs. This approach of using getRangeInElement to target the range of the right-most Alias, while the element was still the parent that contained, but did not go beyond the Alias, was tried after having references only on Aliases and not QualifiedAliases did not fix completion issues. It was the while debugging Go To Declaration actions and noticing they were sensitive to the range in element AND the docs for PsiReference#getRangeInElement that I realized that the Go To Declaration and Completion has a hidden requirement that References for things that behave like namespaces have to work this way.

        • Use ModularName index for Module Variants
          Have a smaller index to iterate and remove need for the isAlias check.
      • Limit Elixir Module resolution to same JetBrains Project Module

        • Change the GlobalSearchScope from allProject to moduleWithDependenciesAndLibrariesScope for faster searching on multi-module projects.
          • Set includeTests based whether the referring element is in a test directory.
        • Use StubIndex#processElements instead of home grown forEachNavigationElement as processElements is more efficient.
      • Favor Module ResolveResults under same Module content roots
        This should favor deps sources in the same module.

      • Iterate function body in unquote(function()) when iterating call definition clauses in quote
        Treats it function body the same as a __using__ body.

      • Standardized preferred ResolveResult filters between Callables and Modules

        1. Prefer valid results as long as it doesn’t leave no results.
        2. Prefer results in the same module as long as it doesn’t leave no results.

        In Go To Declaration also:

        1. Prefer results in source (that aren’t decompiled) as long as it doesn’t leave no results.
      • Use the second argument to use to determine which function is called with apply/3

        This pattern is used in Phoenix __using__, so this lets to differentiate whether Plug.Conn.assign/3 or Phoenix.LiveSocket.assign/3 is resolved in a Controller, LiveComponent, or LiveView.

      • In LEEx Templates

        • Resolve function calls in *.html.leex templates to functions defined in the corresponding LiveComponent/View module.
        • Assigns
          • Resolve assigns in LEEx templates to the keyword key in assign/3 calls in update/2 in the LiveComponent or LiveView
            Had to add back in a TargetElementEvaluator, so that the UnqualifiedNoArgumentCall (name) that is the identifier in an assign (@name) was not counted as valid target for Find Usages / Go To Declaration by itself.
          • Search for assigns in all call definitions in view module
            Expand from just update to any call definition to cover helper functions and other callbacks. Don’t stop on the first valid match because with helper functions and multiple callbacks, the last write isn’t obvious.
          • Resolve assigns set with assign/3
          • Look for assign calls in |> pipelines
          • Resolve assigns set with assign_new/3
          • Resolve @live_action to where it is assigned Phoenix.LiveView.Channel.assign_action/2
          • Find assigns in maps for assign/2
            Since assign/2 accepts either a keyword list or a map, check in both types.
          • Resolve @myself to where it is set in render_pending_components
          • Resolve assigns in macros with do blocks like case and if
          • Find assigns in live_component calls
            If an assign can’t be found in the body of a LiveComponent module, then it maybe passed through from the live_component call itself, so look for any references to the view module (the LiveComponent) and if it is a live_component call, then look at those assigns to try to resolve the assign name.
          • Resolve assigns to live_modal calls
            live_modal is not built into phoenix_live_view, but it is generated by phx.gen.live, so most projects will have it. This allows return_to, which is used use in live_modal, to be resolved.
          • Resolve @socket to last socket variable or call in view module.
          • Resolve @flash to put_flash/3 calls.
          • Resolve @inner_content to Phoenix.LiveView.Utils.to_rendered/2 setting the assign with put_in
        • Resolve aliases in .leex templates
          Generalize the .leex and use walking from call definition clauses to any scope processor lambda, so it can be used to resolve uses of Routes in .leex templates.
      • Resolve Types

        • Have separate references specifically for Types.
        • Resolve to both types declared with @type, @typep, or @opaque; and named type parameters.
        • Process declarations for @type, @typep, and @opaque.
        • Count @callback as declaring a Type
        • Resolve type t for defprotocol to where it is defined in Protocol.__protocol__
          Unfortunately, this ties all protocol’s t to the same element, so Find Usage finds all protocol’s types instead of just a specific module’s type, but this works for projects that have source, but not yet compiled.
          • Favor protocol-specific decompiled @type t when available.
        • Resolve type variable to the keyword key in guards.
        • __MODULE__
      • Favor source over decompiled per name instead of overall for ResolveResultOrderedSet
        This allows the Routes alias to be resolved to the both the MyAppWeb.Router.Helpers in alias MyAppWeb.Router.Helpers, as: Routes in the source of MyAppWeb, but also the decompiled MyAppWeb.Router.Helpers, which is necessary as MyAppWeb.Router.Helpers is produced on compile from router.ex.

      • Resolve references through defdelegate calls.

      • Resolve code_reloading? in MyApp.Endpoint

        The generated MyApp.Endpoint for mix phx.new has a section to enable code-reloading at compile time:

        # Code reloading can be explicitly enabled under the
        # :code_reloader configuration of your endpoint.
        if code_reloading? do
          socket("/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket)
          plug(Phoenix.LiveReloader)
          plug(Phoenix.CodeReloader)
        end
        

        Previously, code_reloading? variable would not resolve because psi.scope.Variable ignored use calls, not use calls are entered and the var!(code_reloading?) is found in Phoenix.Endpoint.config/1 by way of the unquote(config(opts)) call in the quote block in __using__(opts).

      • Resolve qualified calls with unknown resolvable qualifier using only relative identifier and arity.

      • @spec

        • Resolve @spec to defdelegate calls.
        • Resolve @specs to the definitions if the definitions are in a for comprehension
      • Ecto

        • Query
          • Resolve reference variables in Ecto.Query

            • Reference variables are introduced in as the left operand of in passed to from/2 and the join: keyword in from/2.
            • join/3-5
            • Resolve bindings in select/2-3.
            • Resolve reference variables in where/2,3 binding.
              Also add support for resolving a in [{^assoc, a}] binding.
            • Resolve bindings in group_by/2-3.
            • Resolve bindings and expressions in order_by/2-3.
            • Resolve bindings in having/2-3.
            • Add select_merge to declaring Ecto.Query macros.
            • Add distinct/2-3 as a declaring Ecto.Query macro.
          • Resolve field calls in Ecto schema blocks

            How field works in schema for Ecto.Schema

            1. use Ecto.Schema
            2. Ecto.Schema.__using__
            3. `import Ecto.Schema, only: [schema: 2, embedded_schema: 1]``

            Note that only the outer DSLs, schema and embedded_schema are available even though field/2 is defined in Ecto.Schema.

            So when you call schema ... do

            1. defmacro schema(source, [do: block])
            2. schema(__CALLER__, source, true, :id, block)
            3. defp schema(caller, source, meta?, type, block)
            4. There’s a big prelude = quote do quote block

            At the end of prelude there is

            try do
              import Ecto.Schema
              unquote(block)
            after
              :ok
            end
            

            Hey! An import Ecto.Schema, but prelude is just floating as a variable. At the end of defp schema(caller, source, meta?, type, block) is

            quote do
              unquote(prelude)
              unquote(postlude)
            end
            

            So to statically analyze an Ecto.Schema module.

            1. Resolve schema/2 to defmacro schema by walking the use, __using__, quote, and import.
            2. Inside the schema/2 (or macros in general if you want to get fancy :nail_care: and support more DSLs),
            3. Go into the body of the macro. If there’s a call, resolve it
            4. Go into the called function
            5. Look for a quote block at the end (the same as my current __using__ support)
            6. If there’s a Call inside an unquote see if you can resolve it to a variable in addition to a call definition (which is already supported for Phoenix).
            7. If it’s a variable, check it’s value. If it’s a quote, use the quote block handling
            8. In the quote block handling add support for try
            9. Walk the try and see the import, walk the import to find Ecto.Schema.field/2
          • API

            • Resolve Ecto.Query.API functions in Ecto.Query
            • from(order_by: ...)
            • from(select: ...)
              • from(select: tuple())
            • from(where: ...)
            • group_by/2-3
            • having/2-3
            • select/2-3 expr argument
            • where
            • Resolve Ecto.Query.API.fragment to arity interval 0...
            • Resolve fragment nested in other Ecto.Query.API call like type.
            • Walk rightOperand of join(..., ... in ..., ...) for Ecto.Query.API usages like fragment.
      • Resolve module attributes defined in use __using__ quote block

        • Change org.elixir_lang.reference.resolver.ModuleAttribute to use PsiScopeProcessor for non-@for and non-@protocol instead of custom logic.
        • Don’t descend into a use if the ENTRANCE is an ancestor since that means the Alias on the use is probably being resolved.
        • AtUnqualifiedNoParenthesesCall.processDeclarations will call processor.execute when it isn’t a type spec.
        • The UseScopeSelector for AtUnqualifiedNoParenthesesCallImpl has been changed to SELF_AND_FOLLOWING_SIBLINGS since the module attribute is used that way. The previous SELF value was when the UseScopeSelector only applied to variables.
    • “Elixir References” inspection for finding unresolved or invalid references.

      • visitAtNonNumericOperation
        Helps “Elixir References” find unresolved assigns.
    • Strip AccessExpressions from Qualified qualifiers automatically

    • Documentation

      • Show deprecated, impl, and spec, and actual heads in documentation for source functions
      • Merge Documentation across multiple arities when arity of lookup is ambiguous.
    • Find variables defined in EEx element
      Finds variables defined in EEx PsiElement by creating them the same as a StabBody, which means that the siblings are checked backwards from the last parent. This fixes resolving the f from f = form_for ... in *input f ... in LiveView FormComponent templates.

    • Highlight named parameters in types.

    • Decompilation

      • Fake built-in types by decompiling in :erlang.beam
        For type reference resolution of built-in types to work, they need to be defined somewhere. IntelliJ Erlang does this by looping usages back on themselves, but this leads to Find Usages not working for built-in types since each usage is a distinct declaration. By instead defining the types in decompiled :erlang.beam (even though they aren’t actually defined there), there is a shared location for all reference to point to and then check for Find Usages.
      • Decompile types from EEP-48 documentation.
    • Descend into body of unless in quote for treeWalkUp.

    • Descend into for when looking for call definition clauses.

    • QuoteMacro.treeWalkUp into if
      unless was already walked, but not if. Fixes resolving Repo.preload/2, which is defined inside an if in use Ecto.Repo's __using__.

    • Add defguard and defguardp to CallDefinitionClause.is.

    • Don’t generate a reference for Elixir
      Elixir is not declared anywhere. It is the namespace of all Aliases.

    • Refactor how sibling sequences are walked
      Port filtering out comments, white space and end of expressions from siblingExpression.

    • Reimplement fullyQualifiedName

      Eliminate the specialization for ElixirAlias AND QualifiableAlias as which was picked as based on the static casted type. fullyQualifiedName will include the following:

      • Convert __MODULE__ to the enclosing modular canonical name
      • Combine an ElixirAlias with its qualifier
        • If in a ElixirMultipleAliases will combine the qualifier (qualifier.{...}) outside the {} with the relative name inside ...{relative, ...}.
    • EEx

      • viewFile for .eex files used in same directory as .ex using EEx.function_from_file.
      • Resolve function calls to those defined by EEx.function_from_file/3-5.
      • Find view modules for templates under the templates directory.
    • Walk quote macros for sibling call definitions

    • Always fallback to name/arity in any module if callable not resolved in scope

    • Resolve uses of aliased name produced by alias __MODULE__

  • #1967 - @KronicDeth
    • Updates
      • IntelliJ IDEA build version to 2021.1.2
        • Update ParsingTestCase to latest APIs.
      • Gradle plugins
        • intellij 1.0
        • kotlin.jvm 1.5.10
    • Configure runPluginVerifier

Bug Fixes

  • #1948 - @KronicDeth
    • Fix Ctrl+Click/Cmd+B/Go To Declaration for Aliases.

    • Don’t index non-canonical names.
      The non-canonical name for a nested defmodule is the Alias given to that defmodule call. While that name should be resolvable within the parent modular, it should not be indexed because the index is for the global scope.

      Prevents defmodule Enum in

      defmodule ExJsonSchema.Validator.Error do
        ...
        defmodule Enum do
          ...
        end
        ...
      end
      

      from being indexed as Enum, which hides the decompiled version from the standard library because source references are favored over decompiled versions when resolving references.

    • Reference Resolution

      • Only favor same file Module resolutions if they are valid
        Invalid same file results can occur for prefix matches, but if only prefix matches are available, then a whole project search should occur instead of only when there are no same file ResolveResult of either validity.

      • Include library sources in Module#multiResolveProject GlobalSearchScope
        Ensures that library sources can be returned, which are favored over decompiled libraries, so that __using__ macros can be followed.

      • Protect from Module reference element not having a VirtualFile when checking if a test file.

      • Fix QualifiableAliasImpl#fullyQualifiedName

        • Include qualifier of MultipleAliases
          In an alias line like alias Myapp.MyContext.{Schema1, Schema2}, Schema1 would think its fullyQualifiedName was only Schema1, when it should included the fullyQualifiedName of the qualifier, MyApp.MyContext too. This leads to the correct Schema1 fully-qualified name of MyApp.MyContext.Schema1. This fix makes references to Schema1 resolve to both the alias and the defmodule.
        • Include qualifier of MultipleAliases for deep aliases
          In an alias line like alias Myapp.MyContext.{NestedContext1.Schema1, NestedContext2.Schema2}, Schema1 would think its fullyQualifiedName was only NestedContext1.Schema1, when it should included the fullyQualifiedName of the qualifier, MyApp.MyContext too. This leads to the correct Schema1 fully-qualified name of MyApp.MyContext.NestedContext.Schema1. This fix makes references to Schema1 resolve to both the alias and the defmodule.
      • In LEEx Templates

        • Don’t check for implicit imports at top of template file if there is a view file
          The implicit imports should come last, after the view file has been processed.
        • Ignore call definition clauses expressions that can’t contain an assign
          • Atom keywords (false, true, nil)
          • Maps
          • Structs
          • Keyword lists
          • Strings and charlists
        • Don’t follow variables for assigns
          For now, don’t follow variables to look for assigns.
        • Fix finding assigns in |> case do ... end
          None calls were ignored, but when case is used in a pipeline it is a None since it has no literal arguments and only resolved arguments, so it was being ignored.
        • Ignore Lists and Aliases when resolving assigns
        • Fix resolve assigns in ifs
          Only looked in stab of the doBlock, but the else is in the blockList.
      • Don’t use the same path for actual aliases and defmodules when resolving aliases
        Prevents a case of an exact match and its namespace both being marked as valid (exact matches).

      • Types

        • Don’t generate references for map key optionality (required/1 and optional/1)
        • Don’t create a reference to ... in lists in type specs.
        • Look above ElixirStabBody for ancestorTypeSpec.
        • Generate Callable reference for unquote in typespec instead of Type reference.
      • Allow modular names to resolve to multiple modulars
        Fix :systemd.ready() not resolving because :systemd only exists as a decompiled file from source because it is Erlang and there is no preference given to different MIX_ENVs. All MIX_ENV defmodule :systemd are returned for the :systemd and all of them are checked for ready/0.

      • Fix resolving qualified calls that are defined through use

        Resolving qualified calls only used Modular.callDefinitionClauseCallFoldWhile, which ignores all non call definition, which means it ignored the use, but switching to use org.elixir_lang.psi.scope.call_definition_clause.MultiResolve PsiScopeProcessor used for unqualified calls, but starting on the modular call of the qualifier, all the use handling that works for unqualified calls also works for qualified calls now.

        This fixes resolving MyAppWeb.Endpoint.config_change(changed, removed) in MyApp.Application.config_change/3 because use Phoenix.Endpoint, otp_app: :my_app in MyAppWeb.Endpoint is now walked for qualified calls.

      • Fix resolving variables to parameters that are both pattern matched and defaulted

        In functions like

        def f(%Struct{} = s \\ %Struct{}) do
          s
        end
        

        s in the body would not resolve to the s parameter because the default parameter had to be PsiNamedElement, that is a variable directly. Instead, recurse on any operand before the \\ to support pattern matching too.

      • Use keepProcessing() in MultiResolve.addToResolveResults instead of specific return values

        Fixes def in def default_host_flag, do: "--host resolving as an invalid match to default_host_flag instead of search the implicit imports for defmacro def.

      • The nameIdentifier for a defdelegate is the first argument, not the as:
        The as: is the name in the to: module only.

      • Fix defdelegate MultiResolve

        1. I had flipped the head name and as name for checking for a name match
        2. I only counted the defdelegate as matching if the as name as found in to module, but if the head alone is prefixed by the name then the head should be a ResolveResult element even if the to or as can’t be found
        3. Inside the to, I only checked if the children were call definition clauses, but this meant that all the for handling was ignored, so start a new resolveResults() for each modular.
      • Fix toModulars not returning all modulars
        fullyResolve only touched the first modular.

      • Favor complete valid results over incomplete invalid earlier bindings for variables.

      • Ecto

        • Don’t generate reference for assoc/2 pseudo-function in join(..., _ in assoc(_, _))
      • Fix unaliasing multiple aliases
        UnaliasName still assumed that fullyQualifiedName included the qualifier, but it is just the lexical qualifier now.

      • Check for MULTIPLE_ALIASES_QUALIFIER in ResolveState for prefix matches.
        Fixes resolving SSH.Key to Foo.SSH.Key when the alias is alias Foo.{SSH, ...}.

      • Ignore type variables in type restrictions when resolving normal variables.

      • Fix withSelf for childExpressions siblingExpressions call
        While refactoring the walk, I copied the call for siblings, which shouldn’t included self. Children should include self, or the first expression is missed which showed up as Repo.transaction not being found because transaction is the first def inside an if in Ecto.Repo.__using__'s quote block.

      • Don’t try to resolve keys/fields of a capture.

      • Don’t generate reference for assoc in *join*: .. in assoc(..., ...).
        Already worked for join/3 calls, extend to keyword syntax too.

      • Fix resolving references to Modulars when resolved is in BEAM file
        The check to see if the resolved was a modular only worked for source elements because it checked if it was a Call, but decompiled modules in BEAM files are ModuleImpls.

      • Fix logic error when ignored pinned variables as declaration sites

        The logic was supposed to be that

        • If it is pinned, keep searching
        • If it is not pinned, check the operation as a Call

        … but, the code was operatorText != "^" && execute(match as Call, state) which meant that if the operatorText was "^", then the whole expression was false, which meant keepProcessing was false, so any pin stopped the processing for other unpinned parameters after the pin in addition to processing in the outer scope of the pin.

      • Prefer source only for target candidates for Go To Declaration and not all ResolveResults.
        Ensures that functions only defined in decompiled Modules can be found because the Module’s ResolveResults include the decompiled Module in addition to the source ones.

      • Don’t prefer source over decompiled in ResolveResultOrderedSet
        Preference is now handled in Resolver, so that source preference only happens on Go To Declaration and not for multiResolve because that needs decompiled Modulars for compile-time for loops that are too complex to infer.

      • Use primary arity instead of final for resolving Callables

        Fixes unquote in def unquote(...)() do not resolving.

    • Filter out decompiled private functions from completions
      If they are only available as decompiled functions, it is unlikely we have the source available to make them public, which was the reasoning to allow private functions at all.

    • Decompiling

      • Include module name for “No decompiled source function” errors
      • Unquote and when not and/2
        Fixes decompiling :hipe_arm_encode.
      • unquote false when decompiling.
        Fixes decompiling :thrift_json_parser. Add true to the Unquote list too since it would have the same issue.
      • Unquote in when not in/2
        Fix decompiling :digraph_utils
      • Use containingFile.originalFile for resolve.Module.multiResolveProject
        Ensures VirtualFile is available for decompiled files.
    • Performance

      • Don’t use .navigationElement for Variants

        Using navigationElement forces parsing of the decompiled source, which isn’t necessary whe only showing the lookup string.

      • Remove ModuleOwner interface

        • BeamFileImpl is known to have only 1 module in it, so it makes the code more complicated to make it act like source files.
        • Don’t use getChildrenByType, as it causes the decompiled source to be parsed, which is slow and unnecessary since all the metadata for modules is available from the binary format.
      • Get project from parent in decompiled Module and CallDefinition
        Not doing this causes an unnecessary parsing of the decompiled source.

    • Fix infinite recursion in ElixirVisitor#visitLiteralSigilLine

    • Show more scope for keyword key Go To Definition

      • Always show the keyword pair, instead of just the keyword key, so that the value being assigned to the key can be seen before Go To.
      • Show the location at the path relative to the content root and the line number as live_modal may be used more than once in the same file.
    • Put entrance as initial visited in module.Variants.

    • Check if PsiElement has been visited before walking quote and __using__

    • Don’t mark qualified calls as unresolved in “Elixir References” inspection
      I don’t have a good approach for resolving struct fields or map keys for now, don’t mark them as unresolved as it clutters finding unresolved calls I expect to work.

    • Don’t assume PsiFile#virtualFile is NotNull in PsiElement.moduleWithDependentsScope.

    • Don’t assume PsiFile#virtualFile is NotNull in AtNonNumericOperation.computeReference().

    • putInitialVisitedElement in variable.MultiResolve
      Fixes resolving variables through use statements for variables in the body of defmodule blocks.

    • Log when VISITED_ELEMENT_SET is null instead of erroring.

    • Fix resolvePrimaryArity for |> case do
      resolvedPrimaryArity did not add both the doBlock’s arity and the arity from the |> at the same time. If the pipe was there, the do was ignored because the pre-existing resolvePrimaryArity was not used if the normal primary arity of the call was zero, which is the case for |> case do.

    • Find module for elements in libraries
      Elements in libraries don’t have a Module from ModuleUtil.findModuleForPsiElement, so scan the element’s project’s module’s content roots for the closest Module for libraries.

    • prependQualifiers when typing an alias on a new line in the body of a function.

    • Don’t descend into quote blocks if the call being resolved is given to an unquote in the quote block.

  • #1967 - @KronicDeth
    • updateSinceUntilBuild to prevent breaking in future IDEs.
    • Check if modules loaded before using findModuleForPsiElement.
    • Use BasePlatformTestCase instead of LightCodeInsightTestCase because LightCodeInsightTestCase is deprecated.

Installation Instructions

5 Likes

Version 11.12.0

:heart: Sponsor
Historical One-time/Monthly Donations:

Stat Amount
Minimum $1.00
Median $6.25
Mean $12.52
Maximum $200.00

Thanks

Changelog

v11.12.0

Bug Fixes

  • #1988 - @KronicDeth
    • Only descend into modular children of modular for Module scope. Prevents recursion loops on use calls.
    • Don’t search for unquoted variable value variable is value for do:.
    • Protect from IndexNotReady in resolver.Module.multiResolveProject.
    • Stop walking unquoted variable that resolves to a parameter.
    • Walk case in __using__ to find quote in any clause. Fixes resolving test macro from use PowerAssert
    • Resolve functions declared in quote's scope when block injected with unquote(block).
      Fixes resolving field, timestamps, and index in schema for use Yacto.Schema as it makes the block see the import Yacto.Schema above unquote(block) in the quote in schema(..., do: block).
  • #1990 - @KronicDeth
    • Convert MissingSDK errors for Dialyzer into Notifications.
  • #1993 - @KronicDeth
    • Log element in psi.scope.Type instead of using TODO()
      Error will still be reported, but there will be enough information to triage and since true is returned now it won’t stop the type resolving from working.
  • #1994 - @KronicDeth
    • Add missing mix deps options to non-path influencing list:
      • env
      • manager
      • repo
      • sparse
      • submodules
      • system_env
  • #1995 - @hurricup]
    • Fix incorrect OuterElementType in eex.lexer.TemplateData.
      The EEX IElementType needs to use OuterLanguageElementType instead of the direct eex.psi.TokenType.
  • #1996 - @KronicDeth
    • Run QualifiedAlias#text in runReadAction for QualifiedAlias#name
  • #1997 - @KronicDeth
    • Return null Chunk instead of throwing IOException when read incomplete.
      Incomplete reads happen often due to incomplete writes to the file system being read in. As such, they shouldn’t generate error reports and instead should be silently ignored.
  • #1999 - @KronicDeth
    • Implement beam.FileEditor#getFile to fix DeprecatedMethodException as the default implementation is now deprecated and requires an explicit implementation.
    • Use TabbedPaneWrapper.AsJBTabs instead of JBTabbedPane for “BEAM Chunks” tabs.
      I’m not sure why JBTabbedPane stopped showing its labels sometime in the 2020.X IDE version series, but by debugging when “BEAM Chunks” name was retrieved I found that the bottom tabs used TabbedPaneWrapper.asJBTabs. Using that, the labels reappeared.
  • #2000 - @KronicDeth
    • Don’t require Alias qualifier to be a PsiNamedElement.
      It can be an ElixirAtom and getting the reference will still work.
  • #2001 - @KronicDeth
    • Any.isDecompiled for CallDefinitionHead.

Enhancements

  • #1988 - @KronicDeth
    • Find Deps in function calls in the deps() list.

    • Dep.putPath from a variable.

    • Treat Memoize defmemo as def and defmemop as defp.

    • Resolve exception/1 and message/1 to defexception.

    • Resolve to callbacks when searching in any module.

    • Support arity intervals for unquote_splicing in parameters

      Functions defined with unquote_splicing, such as Ecto.Schema.__schema/2:

      for clauses <- Ecto.Schema.__schema__(fields, field_sources, assocs, embeds),
          {args, body} <- clauses do
        def __schema__(unquote_splicing(args)), do: unquote(body)
      end
      

      Need to have their arity not be the number of PsiElements in the parentheses. Any call to unquote_splicing(...) can end up have 0 to infinite parameters, so it means when one is saw, the range of minimum...maximum should change to an open interval of minimum.... This required changing IntRange resolvedFinalArityChange() to ArityInterval resolvedFinalArityInterval() on all Calls, which was a large change. It also meant changing a lot of ArityRange types to ArityInterval, and NameArityRange to NameArityInterval, which influenced the variable names.

      Since all Calls support ArityIntervals now and not just special forms and Ecto DSLs, exportArity is changed to always state the ResolveState, so that the special form changes can be integrated for all callers.

      The actual implementation of CallImpl.resolvedFinalArityRange is changes to fold over the ArityInterval:

      • Normal arguments increase the minimum and maximum.
      • Default arguments increase only the maximum.
      • unquote_splicing changes the maximum to null to indicate the interval is half open.

Installation Instructions

7 Likes

Version 11.13.0

:heart: Sponsor

Historical One-time/Monthly Donations:

Stat Amount
Minimum $1.00
Median $6.25
Mean $12.52
Maximum $200.00

Changelog

v11.13.0

Bug Fixes

Enhancements

  • #2035 - @theqp
    • Update build to IntelliJ IDEA 2021.2.
    • Upgrade to Gradle 7.1.1.
  • #2036 - @KronicDeth
    • Expand compatibility range to 2021.1.2-2021.2.

Installation Instructions

7 Likes

Version 12.0.0

:heart: Sponsor

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 the Level and FilePropertyPusher classes and all their usages, including in the parser grammar and the special ifVersion external 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.
  • #2339 - @KronicDeth

Enhancements

  • #2179 - @KronicDeth
    • Resolve more calls and bindings in Ecto.Query calls
      • Resolve bindings in Ecto.Query.lock/3

      • Resolve bindings in Ecto.Query.windows/3

      • Walk preload binding and expression

      • Resolve in update for Ecto.Query

      • Resolve fragment in with_cte

      • Resolve binding and expr in Ecto.Query.dynamic/1-2

      • Resolve field in join(..., on: field(..., ...) ...)
        For join/5, descend into the options to look for on: value and then walk value the same as the value to having or where in selects since they’re all boolean conditions.

      • Extract ecto.query.Nested

      • Resolve Ecto.Query.WindowAPI functions

      • Resolve reference variable src in join(query, ..., [{src, counter}], ..., ...)
        Tuple lists in join have two forms:

        1. {^assoc, a}
        2. {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 src in (2) could not be resolved.

      • from([..] in ...)

      • Treat or_having the same as having

      • Treat or_where the same as where

      • Treat having: the same as where: in from

      • Treat select_merge the same as select for resolving Ecto.Query.API.

      • from(..., [elements])

      • Resolve Ecto reference variables in left in ...

    • ModuleWalker and NameArityRangeWalker
      Reduce code by abstracting common pattern for walking library APIs.
    • ExUnit
      • Find modules declared in tests.
      • Resolve call definitions inside describe blocks.
      • Resolve variables in assert_receive and assert_received.
      • Resolve alias to modules defined inside the enclosing describe block.
      • Walk assert expression for variable declarations
        • Check for earlier bindings of variables in right operand of = in assert.
    • Resolve require as: 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 and when 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; end to match?(pat1, expr1)
        • Rewrite if var do false else true to !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
    • Resolve module attributes defined outside the immediate modular lexical scope
      • Resolve module attributes registered in elixir_module.erl to 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 quote blocks
    • Resolve variables to variables in any quote blocks
      If a variable can’t be resolved in the scope, try resolving it to any variable declared at the top-level of a quote block. This helps with certain patterns of quote blocks used in Ecto where a variable’s declaration and usage are not in the same quote block.
    • 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 the ElixirStabBody skipped call definitions because they were new scopes.
    • Resolve functions declared with Mix.Generator.embed_template and embed_text.
      Also, new system for tracking resolves paths - imports, defdelegate, and use calls are added to the resolve results after the preferred elements are chosen for source in the same module. This prevents only the import showing because the actual declaration is in another module or the SDK.
    • Record a call as visited in its treeWalkUp instead of requiring the caller to do it in the pass ResolveState
      • Record quoteCall as visited in QuoteMacro.treeWalkUp
      • Record unquoteCall as visited in Unquote.treeWalkUp
      • Record unlessCall as visited in Unless.treeWalkUp
      • Record ifCall as visited in If.treeWalkUp
      • Record importCall as visited in Import.treeWalkUp
      • Record useCall as visited in Use.treeWalkUp
    • More macro specialized in Structure View
      • test
      • describe
    • Resolve Qualifer.unquote(variable)(...) to any definition with correct arity in Qualifier.
    • Only calculate element.resolvedFinalArity once in resolvedQualified
    • Implementations and Protocols
      • Redo icons
      • Implementations Go To Protocol line markers
      • Go to Super for calls to defimpl function/macro
        Goes to corresponding name/arity in the defprotocol that the defimpl implements.
      • 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 def in defprotocol
      • Resolve defp inside of defimpl
        Process declarations inside of implementation the same as modules.
    • Stop prependQualifiers at 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 preferred and expand system from Callables to Modules.
    • Update CI build dependencies
  • #2199 - @KronicDeth
    • Regression test for #2198.
  • #2201 - @KronicDeth
    • Use callbacks as completions for calls.
  • #2223 - @KronicDeth
    • Decompiler
      • Don’t require MacroNameArity for accept, but use NameArity only because no decompiler cares about the macro.
    • Tests for Code.Identifier and String.Tokenizer
  • #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/1 and message/1 defined by defexception
      • *_text/0 and *_template(assigns) functions defined by Mix.Generator.embed_text and Mix.Generator.embed_template.
  • #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
    • 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
        The Throwable is necessary to get a stacktrace, but not a real error.
  • #2339 - @KronicDeth
    • Build against 2021.3
    • runPluginVerifier in 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

Bug Fixes

  • #2074 - @Thau
    • Alternative function clause for put_event with suite_finished
  • #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 if or unless if 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 the ElixirStabBody.

      • Treat child of modulars as being at the same level if nested in if or unless
        Prevents test in if in supervisor_test.exs in ecto from 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 import or use, 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 that fragment/1.. used in a quote can resolve to one in Ecto.Query.API.
    • Resolve variable that are the only child of quote
      Ecto loves doing quote do: query or 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, only quote do: variable would work, but now quote(do: variable) also works to find the quote call.
    • Don’t mark fields and keys that are not expected to be resolvable yet as unresolvable in Elixir References inspection.
      • QualifiedBracketOperation qualifier
      • StructOperation qualifier
      • Expect qualified unquote to only have invalid results.
      • Don’t mark invalid only results for first chained function call.
        Don’t report unquote(schema).__schema__(:source)
      • Don’t mark invalid only results for chained function calls.
        Don’t report Mix.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.Query calls.
      • Add missing state.put(Query.Call, call) for join/3-4 executeOnIn.
      • Walk the operands of |> in select expressions.
      • Resolve pinned variables as normal instead of as reference variables for Ecto.Query calls.
      • Don’t walk keywords that cannot declare reference variables.
        • hints
        • lock
        • intersect
        • intersect_all
        • except
        • except_all
        • union
        • union_all
        • prefix
        • preload
        • offset
        • windows
        • limit
      • Don’t treat signature for call definition as use of Ecto macro
    • Don’t generate references to aliases, functions, or types that don’t have declarations
      • assoc/2 in join: .. in assoc(_, _) in a no parentheses from call
      • var in type restrictions
        Related to elixir-ecto/ecto#3756
      • BitString
        BitString is recognized in defimpl ..., for: BitString to define protocol implementations for <<..>>, but the BitString module itself does not exist, so can’t be resolved.
    • Error reporting
      • Ignore at com.intellij.openapi.diagnostic.Logger when calculating location for error report titles
      • Improve error report format sent to GitHub
      • Fix the event message not 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 at that isn’t from the errorreport.Logger instead of [auto-generated] as this is the pattern I follow when renaming manually.
    • 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 on name and not just on /arity.

      • Don’t allow Mod.name to be acceptable named parent in &Mod.name/arity.
        Resolves #488
        Fixes #2101

        Allows Go To Declaration on name and not just on /arity.

      • Resolve &name/arity and &Mod.name/arity using 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__ in quote to defmacro __MODULE__ in Kernel.SpecialForms
    • Performance
      • Fix String.Unicode decompiled being PlainText instead of Elixir
        String.Unicode when decompiled using all information from DbgI was 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 DbgI if 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 ancestorTypeSpec for 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 walking imports
      Fixes resolving config from use Mix.Config as it delegates to Config
    • Resolve variables used in match? guards to pattern declaration
      Resolves on_delete in match?(%{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) do block. Not having a definition meant that the defimpl protocol, for: struct do would be in AllName index, but not ModularName.
      • Get name of enclosing modular for defimpl without for:
    • Store if stubs are guards
    • Decompiler
      • Surround case statements with parentheses when used in cond clause 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 \x to \\x in OtpErlangStr
      • Fix rewrite of :erlang calls
      • Surround type unions with parentheses
        Prevents parsing problems with unions in guard (when) clauses
      • Don’t use prependIndent because it indents blank lines too.
        This doesn’t match mix format or the IntelliJ Elixir formatting.
      • Erlang
        • Escape fn Erlang variable
        • Escape Erlang char \ as \
        • Don’t append lines for clauses or after in Erlang receive when decompiling if empty.
        • Use Infix, Prefix, and Unquote decompolers for Erlang Abst chunk in addition to DbgI chunk
          • functions
          • typesepcs
        • Use function.macroNameArityMacro.macro when decompiling Erlang Abst clauses.
          Don’t use def anymore when unexported and therefore private; use defp instead.
        • Remove space after ... in decompiled private types.
    • 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 for association.ex in 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.treeWalkUpUnquoted through tuples
    • Quote.treeWalkUp through case
    • 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 tailrec because it doesn’t work correctly for ElixirAccessExpression
    • Stop searching for qualifier when ElixirUnqualifiedNoParenthesesManyArgumentsCall.
  • #2199 - @KronicDeth
    • Stop highlighting types when unquote_splicing/1 is reached.
      unquote_splicing is being used to splat arguments or fields of a struct into the type. The arguments to unquote_splicing are normal calls or variables, not types.
  • #2201 - @KronicDeth
    • Implement call_definition_clause.Variants#executeOnCallback
  • #2204 - @KronicDeth
    • CallDefinitionClause.time/1
      • Mark guards as runtime.
      • Mark anything unknown as runtime too.
      • Log unknown calls.
  • #2207 - @KronicDeth
    • Check if Call isValid before using containingFile for locationString.
  • #2208 - @KronicDeth
    • Check if project is not dumb in nameArityInAnyModule.
  • #2209 - @KronicDeth
    • Take resolveInScope only 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 that Ecto in defmodule Ecto.Adapter do resolved to itself instead of the exact defmodule Ecto do.
  • #2214 - @KronicDeth
    • When regenerating the parser, ElixirVisitor is also regenerated. When it was regenerated it lost the bug fix for #visitLiteralSigileLine calling itself. Added a regression test, so that this can’t happen again.
  • #2223 - @KronicDeth
    • Ecto
      • Walk keyword keys as right operand of in in from
    • 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 in when 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 of Elixir.Phoenix.HTML.Tag.beam
      • Use apply with escaped atom when Erlang function call is an Elixir operator
    • Port String.Tokenizer.tokenize for use in Identifier.inspectAsKey
      I was putting off porting all of Identifer.inspectAsKey by adding special cases as needed, but the decompiler kept having bugs, so port all of it including String.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 left parent in place because the argument is called parent, but since it is an extension function that value because this.parent when it really should have been this. Using this.parent meant it would ask for the parent’s children and keep looping back to this.
    • Don’t use tailrec in function with any body-recursion.
      It causes issues with ElixirAccessExpression recursion sometimes.
  • #2226 - @KronicDeth
    • Implement completion for functions declared with defdelegate.
    • Fix LookupElementPresentation.putItemPresentation addTailText.
      Only append suffix of presentableText if it is prefixed by itemText.
  • #2334 - @KronicDeth
    • Decompiler
      • Elixir
        • Decompile local function calls in Elixir DbgI using inspectAsFunction
          While remote calls used inspectAsFunction, local calls just used the atomValue, which meant names that needed to be unquoted weren’t and caused parsing errors.
      • Erlang Abst
        • Decompile Erlang Abst string with OtpErlangList as strings with non-ASCII codepoints
          Fixes unknown string format in idna.beam
        • Always group for comprehensions in sequence even if there is only 1 element
          Some forms of for comprehensions 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 right as :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
          • bitstring
          • float
          • nonempty_improper_list
          • nonempty_maybe_improper_list
        • Decompile tagged atoms and other complex expression as function name in Abst capture
        • Decompile Erlang Abst float
        • Decompile Erlang Abst begin blocks 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_index when record name needs to be unquoted
        • Decompile map updates 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_type in Erlang Abst
          Fixes decompiling hipe_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
    • 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 return true, but this pre-dated decompiling private functions. Now with decompiling private functions, isExported needs to defer to the Definition and count as unexported if a private function, macro, or guard.
  • #2337 - @KronicDeth
    • Walk map constructin arguments, associatons, and variables when resolving type parameters.
  • #2339 - @KronicDeth
    • Don’t use PluginId.findId that doesn’t exist in 2021.1.X

Installation Instructions

9 Likes

Version 12.0.1

:heart: Sponsor

Historical One-time/Monthly Donations:

Stat Amount
Minimum $1.00
Median $6.25
Mean $12.52
Maximum $200.00

Changelog

v12.0.1

Bug Fixes

  • #2345 - @KronicDeth
    • Override Override org.elixir_lang.*.configuration.Factory.getId to fix deprecation warning about the default implementation being accidentally localizable when it shouldn’t be. The Elixir plugin didn’t localize these name using message bundles, so this wasn’t an actual risk, but overriding is the only way to silence the error.
  • #2346 - @KronicDeth
    • Stop look for qualifier at curly braces for %{Alias}.

Installation Instructions

1 Like

Version 12.1.0

:heart: Sponsor

Historical One-time/Monthly Donations:

Stat Amount
Minimum $1.00
Median $6.25
Mean $12.52
Maximum $200.00

Changelog

v12.1.0

Enhancements

  • #2405 - @osbre
    • Add MacPorts SDK locations to README.
  • #2404 - @KronicDeth
    • Update build to elixir 1.13.0
    • Add support for ** operator to lexer and parser.
      Port elixir-lang/elixir#11241
  • #2436 - @KronicDeth
    • Color settings for each sigil name. Both the interpolating (lower case) and literal (upper case) name use the same settings.
  • #2202 - @KronicDeth
    • Add support for stepped ranges
      Port elixir-lang/elixir#10810
      • Regression test for #2200
    • Update CI
      • Update to Elixir 1.12.3
      • Update to Erlang 24.1.6
  • #2447 - @KronicDeth
    • Use getUsageViewTreeTextRepresentation to check find usages.
    • Regression test for #2374.
  • #2448 - @KronicDeth
    • Regression test for #2386
      Issue #2386 had the same root cause (OtpExternalFun not being decompiled correctly) as Issue #2410, so Issue #2386 was fixed by Pull Request #2441, but since @alexxero was nice enough to upload the .beam file for Issue #2386, I might as well add it as a regression test too.
  • #2453 - @KronicDeth
    • Regression test for #2446

Bug Fixes

  • #2397 - @KronicDeth
    • Restore tests that weren’t being run after 2021.3 upgrade.
      Upgrading to 2021.3 dropped the number of run tests from 3741 to 1452. This was due to a change in the packaging in 2021.3 that triggered a gradle bug.

      • Fix test broken by missing markdown in plugins list
        Like the need for the java plugin to handle the com.intellij.modules.java dependsin plugin.xml, the org.intellij.plugins.markdown depends needed markdown added to the list to make some of the tests work. The failed tests weren’t being run before after the 2021.3 upgrade.
      • Manually include tests instead of scanning.
        Work-around gradle/gradle#18486 as described in Slack that affects running tests with ideaVersion 2021.3.
        • Rename ParsingTestCases with improper capitalization
  • #2404 - @KronicDeth
    • Decompile {:|, ..., ...} as cons operator in Elixir Dbgi.
    • Fix missing guards when decompiling Elixr DbgI clauses using special decompiler
      Guards where missing because only decompiler.appendSignature was used.
    • Fix module.Reference.calculateDefaultRangeInElement
      element.textOffset instead of element.textLength was used for end of range.
  • #2436 - @KronicDeth
    • Restore colors for char lists and strings lost with the PSI changes in e71b247.
  • #2441 - @KronicDeth
    • Render OtpErlangExternalFun correctly as remote captures (&Mod.fun/arity).
      Fixes decompiling Ecto.Changeset.validate_number.
  • #2445 - @KronicDeth
    • Protect ExUnit configuration Producer from ElixirFile without VirtualFile.
  • #2447 - @KronicDeth
    • Unquote.treeWalkUp no longer assumes that the final argument to unquote is a Call.
    • Fix Find Usages for call definitions
      • Don’t resolve primary Find Usages element.
        The primary element passed to FindUsageHandler is now already resolved, so resolving it again for call definitions finds usages of def, defmacro, etc.
      • Since the element passed to FindUsagesProvider#canFindUsageFor, definers can no longer be excluded, so remove that check.
  • #2449 - @KronicDeth
    • Don’t highlight Quote or Sigil as normal text if part of documentation.
      Since the annotators will run in arbitrary order, the Textual annotator has to avoid annotating the same nodes as the ModuleAttribute annotator or the colors can get interleaved.
  • #2450 - @KronicDeth
    • Put ENTRANCE and Initial Visited Element in __module__.Resolver.
  • #2452 - @KronicDeth
    • Keep searching when resolving type parameters if bitstring is encountered.
  • #2453 - @KronicDeth

README Changes

Features

SDK

Package Manager Install Locations

Package Manager SDK Type Directory
MacPorts Elixir SDK /opt/local/lib/elixir
Erlang SDK /opt/local/lib/erlang
Erlang for Elixir SDK

Installation Instructions

6 Likes

Version 12.2.0

:heart: Sponsor

Historical One-time/Monthly Donations:

Stat Amount
Minimum $1.00
Median $6.25
Mean $12.52
Maximum $200.00

Changelog

v12.2.0

Enhancements

  • #2461 - @KronicDeth
    • Test against support IDEA versions in CI to prevent another Find Usage regression
      • 2021.2.3 FindUsageTests differ from manual testings, so don’t test 2021.2.3
      • Make FindUsagesTest UsageViewTreeText work for both 2021.1.3 and other versions
      • Add kernel.ex to all Find Usage Tests to ensure it doesn’t work because it can’t resolve primary element
    • Add find usages for def test

Bug Fixes

  • #2461 - @KronicDeth
    • Work-around Find Usage bug due to temporary change in JetBrains API only in versions that have that change.
      • Restore old find_usages.handler.Call
      • Determine whether to resolve FindUsageHandler primary element based on application build number
        Assume that are 213.* builds have the already resolved call. This excludes 2021.3, but not 2021.3.1, which includes the fix to restore the 2021.1.3 behaviour.
  • #2466 - @KronicDeth
    • Fix HTML errors in description.html.
    • Add card one-liner.

Installation Instructions

3 Likes

Hi.

After upgrading Intellij Idea from v2021.2.4 to v2021.3 it started hanging my IDE when I open some *.ex files, no matter big or small. And it is not clear for what reason.

I’m trying the plugin v12.2.0 + IntelliJ IDEA 2021.3. v12.1.0 does not work either. I’m on Mac.

Also, the last working pair for me is Intellij Idea 2021.2.4 + Elixir plugin v12.0.1.

Is there any way to troubleshoot the issue?

Thanks.

  1. Help > View Logs
  2. Take any threadDump-* and zip them.
  3. Attach the zip to a new issue on GitHub. I can open them with IntelliJ’s own Thread Dump Analyzer to tell me where your IDE is hanging.

@KronicDeth there is any way to force the working directory on Elixir Mix ExUnit build configuration, even when plugin finds a mix.exs file?

My Elixir project needs to run the tests on the root path (top level folder) because I am using Umbrella to manage the projects. If I use this plugin and try to run the tests, it will always set the working directory for the folder where the tests are located.

What I did to use it on my project and run the tests on IDE:
Fix WorkingDirectory on ExUnit Test producer for Umbrella · igorsegallafa/intellij-elixir@0b90d6d (github.com)

The patch you made is only changing the default working director when you right-click to test a line OR click a gutter icon.

The working directory is always customizable in the actual Run Configuration.


If you couldn’t see the Run Configurations before it is because you likely have your Toolbar off in the View. You can turn that on.


Screen Shot 2022-01-15 at 9.55.56 PM
Screen Shot 2022-01-15 at 9.53.31 PM

IIRC it is off by default because it makes the UI less cluttered, but it does mean you have to know the context menus or keyboard shortcuts to customize Run Configurations then

Screen Shot 2022-01-15 at 9.55.38 PM

JetBrains own help docs have the full details on all Run Configuration features.

P.S.

Outside of getting this to work with the plugin, in an umbrella project, your apps should be testable from either the root directory or the specific app directory. If they’re not, you may have your project dependencies and paths configured incorrectly.

2 Likes

Version 12.2.1

:heart: Sponsor

Historical One-time/Monthly Donations:

Stat Amount
Minimum $1.00
Median $6.25
Mean $12.52
Maximum $200.00

Changelog

v12.2.1

Bug Fixes

  • #2579 - @KronicDeth
    • Don’t call .navigationElement to prevent unnecessary decompilation when resolving references or showing
      completions.

      • Don’t call navigationElement in ChooseByNameContributor
      • Don’t decompile Protocols to get their specific t type
      • Don’t call .navigationElement when resolving built-in types in :erlang
      • Don’t use navigationElement in CallDefinitionClause.implicitImports for Kernel and
        KernelSpecial.Forms.
      • Don’t call .navigationElement in reference.resolver.Module.multiResolveProject
        Prevents decompiling modules when doing completion.

      Handle PsiCompiledElement in places that previously only handled source and decompiled Calls

      • getElementDescription for ModuleImpl
      • Complete call definition under binary modules
      • Don’t look for variables in compiled elements
      • Don’t assume elements have a non-null node Decompiled elements don’t
      • Index TypeDefinitions in .beam files Only supported built-in types faked in :erlang for now
      • Walk ModuleImpl for scope.Type

Installation Instructions

3 Likes

Version 13.0.0

:heart: Sponsor

Historical One-time/Monthly Donations:

Stat Amount
Minimum $1.00
Median $6.25
Mean $12.52
Maximum $200.00

Changelog

v13.0.0

Incompatible Changes

Enhancements

  • #2594 - @KronicDeth
    • Support 2022.1 IDEs
      • Update IntelliJ Plugin Gradle to 1.5.3.
      • Set untilBuild explicitly
      • Target JVM Version 11
  • #2633 - @KronicDeth
    • Expand untilBuild to cover PATCH releases of 2022.1.

Bug Fixes

  • #2594 - @KronicDeth
    • Fix (some) plugin verifier warnings
      • Fix (some) deprecation warnings.
      • Use fewer Internal APIs.
  • #2633 - @KronicDeth
    • Don’t use ElixirModuleType to check if it is available.
      It will not load in IDEs that lack JavaModuleBuilder, which is why I was hard coding the ran previously.
    • Don’t use XmlSerializer as equivalent of DefaultJDOMExternalizer
      The DefaultJDOMExternalizer method calls weren’t writing anything and the explicit attribute value accesses were,
      so it is more correct to just remove the original deprecated calls.

Installation Instructions

2 Likes