ElixirLS is provides two components: a language server driving code intelligence and a debug adapter that allows step through debugging of Elixir projects. Language server adheres to the Language Server Protocol. Debug adapter implements Debug Adapter Protocol.
This repo is a community maintained fork. The original repository JakeBecker/elixir-ls has now been deprecated in favor of this one.
Note: On its first run, Dialyzer will build a PLT cache. This will take a considerable amount of CPU time (usually 10+ minutes). After that is complete, the CPU usage will go back to normal. Alternatively, instead of waiting you can disable Dialyzer in the settings.
IDE | Plugin | Support |
---|---|---|
BBEdit | bbpackage | |
Emacs | eglot | |
Emacs | lsp-mode | Supports debug adapter via dap-mode |
Kakoune | kak-lsp | Limitations |
Kate | built-in LSP Client plugin | Does not support debug adapter |
Neovim | coc.nvim | Does not support debug adapter |
Neovim | nvim-dap | Supports debug adapter only |
Neovim | nvim-lspconfig | Does not support debug adapter |
Nova | nova-elixir-ls | |
Sublime Text | LSP-elixir | Does not support debug adapter |
Vim/Neovim | ALE | Does not support debug adapter or @spec suggestions |
Vim/Neovim | elixir-lsp/coc-elixir | Does not support debug adapter |
Vim/Neovim | vim-lsp | Does not support debug adapter |
VS Code | elixir-lsp/vscode-elixir-ls | Supports all ElixirLS features |
Helix | elixir-lsp | Supports all ElixirLS features |
Please feel free to create and publish your own client packages and add them to this list!
The installation process for ElixirLS depends on your editor.
Elixir itself supports five versions with security updates: https://hexdocs.pm/elixir/compatibility-and-deprecations.html#content
OTP supports the last three versions: http://erlang.2086793.n4.nabble.com/OTP-Versions-and-Maint-Branches-td4722416.html
ElixirLS generally aims to support the last three released versions of Elixir and the last three versions of OTP. However this is not a hard and fast rule and may change in the future.
OTP Versions | Elixir Versions | Supports ElixirLS | Issue(s) |
---|---|---|---|
any | <= 1.12 | No | No support for Code.Fragment |
22 | 1.12 - 1.13 | Yes | Erlang docs not working (requires EIP 48) |
23 | 1.12 - 1.14 | Yes | None |
24 | 1.12 - 1.16 | Yes | None |
25 | 1.13.4 - 1.17 | Yes | None |
26.0.0 - 26.0.1 | any | No | #886 |
26.0.2 - 26.1.2 | 1.14.5 - 1.17 | *nix only | #927, #1023 |
>= 26.2.0 | 1.14.5 - 1.17 | Yes | None |
any | 1.15.5 | Yes | Broken formatter #975 |
27 | 1.17 | Yes | None |
It is generally recommended to install Elixir and Erlang via ASDF so that you can have different projects using different versions of Elixir without having to change your system-installed version. ElixirLS can detect and use the versions of Elixir and Erlang that you have configured in ASDF.
ElixirLS provides debug adapter support adhering to the Debug Adapter Protocol, which is closely related to the Language Server Protocol.
When debugging in Elixir or Erlang, only modules that have been "interpreted" (using :int.ni/1
or :int.i/1
) will accept breakpoints or show up in stack traces. The debugger in ElixirLS automatically interprets all modules in the Mix project and its dependencies before launching the Mix task. Therefore, you can set breakpoints anywhere in your project or dependency modules.
Please note that there is currently a limit of 100 breakpoints.
To debug modules in .exs
files (such as tests), they must be specified under requireFiles
in your launch configuration so that they can be loaded and interpreted before running the task. For example, the default launch configuration for mix test
in the VSCode plugin is shown below:
{
"type": "mix_task",
"name": "mix test",
"request": "launch",
"task": "test",
"taskArgs": ["--trace"],
"startApps": true,
"projectDir": "${workspaceRoot}",
"requireFiles": ["test/**/test_helper.exs", "test/**/*_test.exs"]
}
Currently, to debug a single test or a single test file, it is necessary to modify taskArgs
and ensure that no other tests are required in requireFiles
.
{
"type": "mix_task",
"name": "mix test",
"request": "launch",
"task": "test",
"taskArgs": ["tests/some_test.exs:123"],
"startApps": true,
"projectDir": "${workspaceRoot}",
"requireFiles": ["test/**/test_helper.exs", "test/some_test.exs"]
}
To debug Phoenix applications using ElixirLS, you can use the following launch configuration:
{
"type": "mix_task",
"name": "phx.server",
"request": "launch",
"task": "phx.server",
"projectDir": "${workspaceRoot}"
}
Please make sure that startApps
is not set to true
. To clarify, startApps
is a configuration option in the ElixirLS debug adapter. It controls whether or not to start the applications in the Mix project before running the task. In the case of Phoenix applications, setting startApps
to true
can interfere with the application's normal startup process and cause issues.
If you are running tests in the Phoenix application, you may need to set startApps
to true. This will ensure that the necessary applications are started before the tests run.
It's important to note that NIF (Native Implemented Function) modules cannot be interpreted due to limitations in :int
. Therefore, these modules need to be excluded, using the excludeModules
option. This option can also be used to disable interpretation for specific modules when it's not desirable, such as when performance is unsatisfactory.
{
"type": "mix_task",
"name": "mix test",
"request": "launch",
"task": "test",
"taskArgs": ["--trace"],
"projectDir": "${workspaceRoot}",
"requireFiles": ["test/**/test_helper.exs", "test/**/*_test.exs"],
"excludeModules": [":some_nif", "Some.SlowModule"]
}
Function breakpoints in ElixirLS allow you to break on the first line of every clause of a specific function. In order to set a function breakpoint, you need to specify the function in the format of MFA (module, function, arity).
For example, to set a function breakpoint on the foo
function in the MyModule
module that takes one argument, you would specify it as MyModule.foo/1
.
Please note that function breakpoints only work for public functions and do not support breaking on private functions.
Break conditions allow you to specify an expression that, when evaluated, determines whether the breakpoint should be triggered or not. The expression is evaluated within the context of the breakpoint, which includes all bound variables.
For example, you could set a breakpoint on a line of code that sets a variable x
, adding a break condition of x > 10
. This would cause the breakpoint to trigger when that line of code is executed, but only if the value of x
is greater than 10
when that line of code is executed.
However, it's important to note that the expression evaluator used by ElixirLS has some limitations. For example, it doesn't support some Elixir language features, such as macros and some built-in functions. In addition, the expression evaluator is not as powerful as the one used by the Elixir interpreter, so some expressions that work in the interpreter may not work in ElixirLS.
A "hit condition" is an optional parameter that can be set on a breakpoint to control how many times a breakpoint should be hit before stopping the process. It is expressed as an integer and can be used to filter out uninteresting hits, allowing the process to continue until a certain condition is met.
For example, if you have a loop that runs 10 times and you want to stop the process only when the loop reaches the 5th iteration, you can set a breakpoint with a hit condition of five. This will cause the breakpoint to be hit only on the 5th iteration of the loop; the process will continue to run until then.
"Log points" are a type of breakpoint that logs a message to the standard output without stopping program execution. When a log point is hit, the message is evaluated and printed to the console. The message can include interpolated expressions enclosed in curly braces {}
, e.g. my_var is {inspect(my_var)}
. These expressions will be evaluated in the context of the breakpoint. To escape the curly braces, you can use the escape sequence \{
and \}
.
It's important to note that as of version 1.51 of the Debug Adapter Protocol specification, log messages are not supported on function breakpoints.
The debugger's expression evaluator has some limitations due to how the Erlang VM works. Specifically, the evaluator is implemented using :int
, which works at the level of individual BEAM instructions. As a result, it returns multiple versions of variables in Static Single Assignment form, without indicating which one is valid in the current Elixir scope.
To work around this, the evaluator uses a heuristic to select the highest versions of variables. However this doesn't always behave correctly in all cases. For example, in the following code snippet:
a = 4
if true do
a = 5
end
some
If a breakpoint is set on the line with some_function()
, the last bound value for a
seen by the expression breakpoint evaluator will be 5
, even though it should be 4
.
Additionally, although all bound variables are accessible in the expression evaluator, the evaluator doesn't support accessing module attributes (because these are determined at compile time).
It may be useful to connect to a running debug adapter node via OTP distribution. This enables inspecting the running application and remotely triggering debugged functions. In order to do so, set ELS_ELIXIR_OPTS
in the launch configuration and pass in the appropriate node name/sname
and cookie
.
{
"env": {
"ELS_ELIXIR_OPTS": "--name mynode@localhost --cookie secret"
}
}
ElixirLS debug adapter is capable of remote debugging OTP cluster nodes. This functionality relies on OTP debugger. In order to attach to a remote node some@host
a special launch config with request attach
is needed. The launch config must specify remoteNode
as well as cookie
and name
or sname
for local DAP node.
{
"type": "mix_task",
"name": "attach",
"request": "attach",
"projectDir": "${workspaceRoot}",
"remoteNode": "some@host",
"debugAutoInterpretAllModules": false,
"debugInterpretModulesPatterns": ["MyApp.*"],
"env": {
"ELS_ELIXIR_OPTS": "--sname elixir_ls_dap --cookie mysecret"
}
}
debugger
application is loadable on remote node. This may require including it in extra_applications
strip_beams
set to false
. Note that it defaults to true
debug_info
set to false
via elixirc_options
or @compile
attributeRemote debugger has several limitations compared to local debugger:
dbg
macro breakpoints are not supportedElixirLS debug adapter interprets modules with :int.ni/1
on all connected nodes. It attempts to uninterpret all modules on debug session end but that may not be possible due to loss of connectivity. This may affect production workloads. Use remote debugging with caution.
ElixirLS provides automatic builds and error reporting. By default, builds are triggered automatically when files are saved, but you can also enable "autosave" in your IDE to trigger builds as you type. If you prefer to disable automatic builds, you can set the elixirLS.autoBuild
configuration option to false
.
Internally, ElixirLS uses the mix compile
task to compile Elixir code. When errors or warnings are encountered during compilation, they are returned as LSP diagnostics. Your IDE may display them inline in your code as well as in the "Problems" pane. This allows you to quickly identify and fix errors in your code as you work.
Dialyzer is a static analysis tool used to identify type discrepancies, unused code, unreachable code, and other warnings in Erlang and Elixir code. ElixirLS provides automatic integration with Dialyzer to help catch issues early on in the development process.
After each successful build, ElixirLS automatically analyzes the project with Dialyzer and maintains a "manifest" file in .elixir_ls/dialyzer_manifest to store the results of the analysis. The initial analysis of a project can take a few minutes, but subsequent analyses are usually very fast, often taking less than a second. ElixirLS also looks at your modules' abstract code to determine whether they reference any modules that haven't been analyzed and includes them automatically.
You can control which warnings are shown by using the elixirLS.dialyzerWarnOpts
setting in your project or IDE's settings.json
. You can find available options in dialyzer documentation, under the section "Warning options".
To disable Dialyzer completely, set elixirLS.dialyzerEnabled
to false.
If Dialyzer gets stuck and emits incorrect or outdated warnings, it's best to restart the language server.
ElixirLS provides an advanced code completion provider. This provider uses two main mechanisms to provide suggestions to the user.
The first mechanism is reflection, which involves getting information about compiled modules from the Erlang and Elixir APIs. This mechanism provides precise results, but it is not well suited for on-demand completion of symbols from the currently edited file. The compiled version of the code may be outdated or the file may not even compile, which can lead to inaccurate results.
The second mechanism used by the code completion provider is AST analysis of the current text buffer. This mechanism helps in cases where reflection is not accurate enough (e.g., completing symbols from the currently edited file). However, it also has its limitations. Due to the metaprogramming-heavy nature of Elixir, it is infeasible to be 100% accurate with AST analysis.
The completions include:
With Dialyzer integration enabled, ElixirLS will build an index of symbols (modules, functions, types, and callbacks). The symbols are taken from the current workspace, all dependencies, and stdlib (Elixir and Erlang). This feature enables quick navigation to symbol definitions.
Below is a list of configuration options supported by the ElixirLS language server. Please refer to your editor's documentation to determine how to configure language servers.
race_conditions
option is unsupported.@spec
annotations inline, using Dialyzer's inferred success typings (Requires Dialyzer).true
(enabled).Below is a list of configuration options supported by the ElixirLS Debug Adapter. Configuration options can be supplied via launch configuration. Please refer to your editor's documentation on how to configure debug adapters.
mix app.start
before launching the debugger. Some tasks (such as Phoenix tests) expect apps to already be running before the test files are required. Defaults to false
.:default_task
key in mixfile.true
.:int.stack_trace/1
. See :int.stack_trace/1 for details. Allowed values are all
, no_tail
, and false
.${workspaceRoot}
can be used.false
setting. Defaults to true
.false
.true
.Basic troubleshooting steps:
hex
and git
installed.github.com
and hex.pm
are accessible. You may need to configure an HTTPS proxy. If your setup uses TLS man-in-the-middle inspection, you may need to set HEX_UNSAFE_HTTPS=1
.Mix.install
directory (location on your system can be obtained by calling Path.join(Mix.Utils.mix_cache(), "installs")
from iex
session)restart
.mix clean
or mix clean --deps
in ElixirLS with the custom command mixClean
..elixir_ls
directory, then restart your editor.
You may need to set elixirLS.mixEnv
, elixirLS.mixTarget
, and elixirLS.projectDir
if your project requires this. By default, ElixirLS compiles code with MIX_ENV=test
and MIX_TARGET=host
; it assumes that mix.exs
is located in the workspace root directory.
If you get an error like the following immediately on startup:
[Warn - 1:56:04 PM] ** (exit) exited in: GenServer.call(ElixirLS.LanguageServer.JsonRpc, {:packet, %{...snip...}}, 5000)
** (EXIT) no process: the process is not alive or there's no process currently associated with the given name, possibly because its application isn't started
and you installed Elixir and Erlang from the Erlang Solutions repository, you may not have a full installation of Erlang. This can be solved with sudo apt-get install esl-erlang
. (This was originally reported in #208).
On Fedora Linux, if you only install the Elixir package you will not have a full Erlang installation. This can be fixed by running sudo dnf install erlang
(This was reported in #231).
.exs
files don't return compilation errors..elixir_ls
directory to fix).scope
of a Phoenix router.There are two ways of building the release: Mix.install
based (recommended) and .ez
archives (deprecated).
Mix.install
based releasemix deps.get
MIX_ENV=prod mix compile
MIX_ENV=prod mix elixir_ls.release2 -o <release_dir>
This copies language server and debugger adapter launch scripts to the <release_dir>
and includes a VERSION
manifest file. The launch scripts install a release specified by the version manifest via Mix.install
and then launch it. This ensures that ElixirLS is built with the correct combination of Elixir and OTP.
.ez
archives releasemix deps.get
MIX_ENV=prod mix compile
MIX_ENV=prod mix elixir_ls.release -o <release_dir>
This builds the language server and debugger as a set of .ez
archives and creates .sh
and .bat
scripts to launch them.
If you're packaging these archives in an IDE plugin, be sure to build using the minimum supported OTP version. This will provide the best backwards compatibility.
This section provides additional information on how to set up the ElixirLS locally.
When launching ElixirLS from an IDE that is itself launched from a graphical shell, the environment may not be complete enough to find or run the correct Elixir/OTP version. To address this on Unix or Linux, the ElixirLS wrapper scripts try to configure ASDF (a version manager for Elixir and other languages), but that may not always be what is needed.
To ensure that the correct environment is set up, you can create a setup script. The setup script location varies based on platform and shell:
$XDG_CONFIG_HOME/elixir_ls/setup.sh
(by default ~/.config/elixir_ls/setup.sh
)$XDG_CONFIG_HOME/elixir_ls/setup.fish
(by default ~/.config/elixir_ls/setup.fish
)%APPDATA%\elixir_ls\setup.bat
In the setup script, the environment variable ELS_MODE
is available and set to either debug_adapter
or language_server
to help you decide what to do.
Note: The setup script must not read from stdin
or write to stdout
. On Unix, Linux, and macOS
this might be accomplished by adding >/dev/null
at the end of any line that produces
output; for a Windows batch script, you will want to add @echo off
at the top and use >nul
.
If you want to debug your setup script you can write to stderr.
Please refer to DEVELOPMENT.md.
ElixirLS supports the following environment variables.
1
, this will make ElixirLS run a local release. If this is not set, a published release matching VERSION
will be used (default).ElixirLS language server sends telemetry information to the client via LSP Telemetry notification, DAP Output event and DAP ErrorResponse. Telemetry data include usage, performance, environment info and error reports. Please refer to your client and/or extension documentation on telemetry.
ElixirLS incorporates code intelligence providers that were originally developed in Elixir Sense and still uses this library for lower lever operations. Other prior work includes Alchemist Server, Elixir plugin for Atom, VSCode Elixir. Credit for those projects goes to their respective authors.
ElixirLS source code is released under Apache License 2.0.
See LICENSE for more information.
ElixirLS includes parts of other projects, please see the respective licenses which apply to them.