diff --git a/CHANGELOG.develop b/CHANGELOG.develop index d75f96fd9..32a9472c5 100644 --- a/CHANGELOG.develop +++ b/CHANGELOG.develop @@ -1411,6 +1411,7 @@ Other: - Change binding of =alchemist-project-find-test= from ~SPC m p t~ to ~SPC m t F~ (thanks to Lyuben Petrov) - Mark alchemist jump handler as async (thanks to Lukasz Czaplinski) +- Added elixir-ls backend (thanks to mpanarin) **** Elm - Fixed flycheck initialization (thanks to Kevin W. van Rooijen) - Added =elm-test-runner= (thanks to Juan Edi) diff --git a/layers/+lang/elixir/README.org b/layers/+lang/elixir/README.org index da84a1ce6..7e8a20bf7 100644 --- a/layers/+lang/elixir/README.org +++ b/layers/+lang/elixir/README.org @@ -2,55 +2,106 @@ #+TAGS: general|layer|multi-paradigm|programming -[[file:img/elixir.png]] with [[file:img/alchemist.png]] +[[file:img/elixir.png]] * Table of Contents :TOC_5_gh:noexport: - [[#description][Description]] - [[#features][Features:]] - [[#install][Install]] + - [[#layer][Layer]] + - [[#choosing-a-backend][Choosing a backend]] + - [[#feature-comparison-in-backends][Feature comparison in backends]] - [[#configuration][Configuration]] - [[#flycheck][Flycheck]] - [[#credo][Credo]] - [[#dogma][Dogma]] - [[#mix-compile][mix compile]] + - [[#language-server-protocol][Language server protocol]] + - [[#debugger][Debugger]] - [[#key-bindings][Key bindings]] - - [[#refcard][Refcard]] - - [[#help][Help]] - - [[#mix][Mix]] - - [[#project][Project]] - - [[#evaluation-in-place][Evaluation in place]] - - [[#repl-interactions][REPL interactions]] - - [[#tests][Tests]] - - [[#compile][Compile]] - - [[#execute][Execute]] - - [[#code-definition-jump][Code Definition Jump]] - - [[#hex-packages][Hex (packages)]] - - [[#macro-expand][Macro expand]] - - [[#formatting][Formatting]] + - [[#alchemist][Alchemist]] + - [[#refcard][Refcard]] + - [[#help][Help]] + - [[#mix][Mix]] + - [[#project][Project]] + - [[#evaluation-in-place][Evaluation in place]] + - [[#repl-interactions][REPL interactions]] + - [[#tests][Tests]] + - [[#compile][Compile]] + - [[#execute][Execute]] + - [[#code-definition-jump][Code Definition Jump]] + - [[#hex-packages][Hex (packages)]] + - [[#macro-expand][Macro expand]] + - [[#formatting][Formatting]] + - [[#lsp][LSP]] * Description This layer adds support for [[http://elixir-lang.org/][Elixir]]. [[https://github.com/tonini/alchemist.el][Alchemist]] brings the Elixir tooling to Emacs and comes with a bunch of features. +[[https://github.com/emacs-lsp/lsp-mode][Lsp-mode]] brings IDE like features following Language Server Protocol, through [[https://github.com/JakeBecker/elixir-ls][elixir-ls]] + +As Alchemist is no longer maintained, elixir-ls is a preferred solution, even though it has less features at the moment. ** Features: -- Powerful IEx integration -- Mix integration -- Compile & Execution of Elixir code -- Inline code evaluation -- Documentation lookup -- Definition lookup -- Smart code completion -- Elixir project management -- Integration with [[http://company-mode.github.io/][company-mode]] -- Flycheck support for mix compile -- Flycheck support for [[https://github.com/rrrene/credo][credo]] -- Flycheck support for test results + - Powerful IEx integration + - Mix integration + - Compile & Execution of Elixir code + - Inline code evaluation + - Documentation lookup + - Definition lookup + - Smart code completion + - Elixir project management + - Integration with [[http://company-mode.github.io/][company-mode]] + - Flycheck support for mix compile + - Flycheck support for [[https://github.com/rrrene/credo][credo]] + - Flycheck support for test results * Install -To use this configuration layer, add it to your =~/.spacemacs=. You will need to -add =elixir= to the existing =dotspacemacs-configuration-layers= list in this -file. +** Layer + To use this configuration layer, add it to your =~/.spacemacs=. You will need to + add =elixir= to the existing =dotspacemacs-configuration-layers= list in this + file. +** Choosing a backend + To choose a default backend set the layer variable =elixir-backend=: + + #+BEGIN_SRC elisp + (elixir :variables elixir-backend 'alchemist) + #+END_SRC + + Backend can be chosen on a per project basis using directory local variables + (files named =.dir-locals.el= at the root of a project), an example to use the + =lsp= backend: + + #+BEGIN_SRC elisp + ;;; Directory Local Variables + ;;; For more information see (info "(emacs) Directory Variables") + + ((elixir-mode (elixir-backend . lsp))) + #+END_SRC + + *Note:* you can easily add a directory local variable with ~SPC f v d~. + + The available options are: + + | symbol | description | + |------------+-------------------| + | 'alchemist | Default | + | 'lsp | elixir-ls package | + +*** Feature comparison in backends + | features | Alchemist | Lsp | + |------------------------------------+-----------+-----| + | Powerful IEx integration | yes | no | + | Mix integration | yes | no | + | Compile & Execution of Elixir code | yes | no | + | Inline code evaluation | yes | no | + | Documentation lookup | yes | yes | + | Definition lookup | yes | yes | + | Smart code completion | yes | yes | + | Elixir project management | yes | no | + | Integration with =company-mode= | yes | yes | + | Flycheck support | yes | yes | * Configuration ** Flycheck @@ -125,130 +176,156 @@ Emacs won't ask you if the variable is safe whenever an elixir file is opened. Remember that you can verify the flycheck checkers status with ~SPC e v~. +** Language server protocol + The =lsp= backend uses [[https://github.com/JakeBecker/elixir-ls][elixir-ls]] as its language server implementation + Clone the project to your system and follow the building instructions [[https://github.com/JakeBecker/elixir-ls#building-and-running][here]] + Or the lsp-elixir [[https://github.com/elixir-lsp/elixir-ls][fork]], which is actively maintained as well + + set the =elixir-ls-path= to the release folder of =elixir-ls= in .spacemacs =dotspacemacs/user-config= + #+BEGIN_SRC elisp + (setq elixir-ls-path "*path to folder*/elixir-ls/release") + #+END_SRC + or in the layer definition + #+BEGIN_SRC elisp + (elixir :variables elixir-backend 'alchemist elixir-ls-path "*path to folder*/elixir-ls/release") + #+END_SRC + + by default =elixir-ls-path= is equal to ="~/elixir-ls/release"= + +** Debugger + For =lsp= backend only + + =lsp= backend supports debugging of your elixir project through [[https://github.com/emacs-lsp/dap-mode][dap]] + simply follow [[https://github.com/syl20bnr/spacemacs/tree/develop/layers/%2Btools/dap#layer-installation][dap layer]] installation and configuration instructions + as well as make sure to read this elixir-ls [[https://github.com/elixir-lsp/elixir-ls#debugger-support][debugger support note]] + * Key bindings -** Refcard -You find and overview of all the key bindings on the [[https://github.com/tonini/alchemist.el/blob/master/doc/alchemist-refcard.pdf][Alchemist-Refcard]]. +** Alchemist +*** Refcard + You find and overview of all the key bindings on the [[https://github.com/tonini/alchemist.el/blob/master/doc/alchemist-refcard.pdf][Alchemist-Refcard]]. -** Help +*** Help -| Key binding | Description | -|-------------+-------------------------------------| -| ~SPC m h :~ | Run custom search for help | -| ~SPC m h h~ | Show help of the current expression | -| ~SPC m h H~ | Toggle through search history | -| ~SPC m h r~ | Show help for current region | + | Key binding | Description | + |-------------+-------------------------------------| + | ~SPC m h :~ | Run custom search for help | + | ~SPC m h h~ | Show help of the current expression | + | ~SPC m h H~ | Toggle through search history | + | ~SPC m h r~ | Show help for current region | -** Mix +*** Mix -| Key binding | Description | -|-------------+------------------------------------------------------------| -| ~SPC m m :~ | Prompt for a =mix= command | -| ~SPC m m c~ | Compile the whole application | -| ~SPC m m h~ | Show help for a specific =mix= command | -| ~SPC m m x~ | Run the given expression in the Elixir application context | + | Key binding | Description | + |-------------+------------------------------------------------------------| + | ~SPC m m :~ | Prompt for a =mix= command | + | ~SPC m m c~ | Compile the whole application | + | ~SPC m m h~ | Show help for a specific =mix= command | + | ~SPC m m x~ | Run the given expression in the Elixir application context | -** Project +*** Project -| Key binding | Description | -|-------------+------------------------------------------------------------| -| ~SPC m g t~ | Toggle between a file and its tests in the current window. | -| ~SPC m g T~ | Toggle between a file and its tests in other window. | + | Key binding | Description | + |-------------+------------------------------------------------------------| + | ~SPC m g t~ | Toggle between a file and its tests in the current window. | + | ~SPC m g T~ | Toggle between a file and its tests in other window. | -** Evaluation in place +*** Evaluation in place -| Key binding | Description | -|-------------+-----------------------------------------| -| ~SPC m e b~ | Evaluate buffer | -| ~SPC m e B~ | Evaluate buffer and insert result | -| ~SPC m e l~ | Evaluate current line | -| ~SPC m e L~ | Evaluate current line and insert result | -| ~SPC m e r~ | Evaluate region | -| ~SPC m e R~ | Evaluate region and insert result | + | Key binding | Description | + |-------------+-----------------------------------------| + | ~SPC m e b~ | Evaluate buffer | + | ~SPC m e B~ | Evaluate buffer and insert result | + | ~SPC m e l~ | Evaluate current line | + | ~SPC m e L~ | Evaluate current line and insert result | + | ~SPC m e r~ | Evaluate region | + | ~SPC m e R~ | Evaluate region and insert result | -** REPL interactions +*** REPL interactions -| Key binding | Description | -|-------------+-----------------------------------------------------------------| -| ~SPC m s c~ | Compiles the current buffer in the IEx process. | -| ~SPC m s i~ | Start an =iex= inferior process | -| ~SPC m s I~ | Start an IEx process with mix (=iex -S mix=) | -| ~SPC m s l~ | Send current line to REPL buffer | -| ~SPC m s L~ | Send current line to REPL buffer and focus it in =insert state= | -| ~SPC m s m~ | Reloads the module in the current buffer in your IEx process | -| ~SPC m s r~ | Send region to REPL buffer | -| ~SPC m s R~ | Send region to REPL buffer and focus it in =insert state= | + | Key binding | Description | + |-------------+-----------------------------------------------------------------| + | ~SPC m s c~ | Compiles the current buffer in the IEx process. | + | ~SPC m s i~ | Start an =iex= inferior process | + | ~SPC m s I~ | Start an IEx process with mix (=iex -S mix=) | + | ~SPC m s l~ | Send current line to REPL buffer | + | ~SPC m s L~ | Send current line to REPL buffer and focus it in =insert state= | + | ~SPC m s m~ | Reloads the module in the current buffer in your IEx process | + | ~SPC m s r~ | Send region to REPL buffer | + | ~SPC m s R~ | Send region to REPL buffer and focus it in =insert state= | -** Tests +*** Tests -| Key binding | Description | -|-------------+---------------------------------------------------------------------------------------| -| ~SPC m g t~ | Open the test file for current buffer | -| ~SPC m t a~ | Run all the tests | -| ~SPC m t b~ | Run all the tests from current buffer | -| ~SPC m t B~ | Run all the tests from current file; if test file not found, after confirm, create it | -| ~SPC m t f~ | Choose test file to run | -| ~SPC m t t~ | Run test under point | -| ~SPC m t r~ | Rerun the last test | -| ~SPC m t n~ | Jump to next test | -| ~SPC m t N~ | Jump to previous test | -| ~SPC m t s~ | Run stale tests (~mix test --stale~) | -| ~SPC m t R~ | Toggle test report window | -| ~SPC m t F~ | Open project test directory and list all test files. | + | Key binding | Description | + |-------------+---------------------------------------------------------------------------------------| + | ~SPC m g t~ | Open the test file for current buffer | + | ~SPC m t a~ | Run all the tests | + | ~SPC m t b~ | Run all the tests from current buffer | + | ~SPC m t B~ | Run all the tests from current file; if test file not found, after confirm, create it | + | ~SPC m t f~ | Choose test file to run | + | ~SPC m t t~ | Run test under point | + | ~SPC m t r~ | Rerun the last test | + | ~SPC m t n~ | Jump to next test | + | ~SPC m t N~ | Jump to previous test | + | ~SPC m t s~ | Run stale tests (~mix test --stale~) | + | ~SPC m t R~ | Toggle test report window | + | ~SPC m t F~ | Open project test directory and list all test files. | -** Compile +*** Compile -| Key binding | Description | -|-------------+----------------------------------------------------| -| ~SPC m c :~ | Run a custom compile command with =elixirc= | -| ~SPC m c b~ | Compile the current buffer with elixirc. =elixirc= | -| ~SPC m c f~ | Compile the given filename with =elixirc= | + | Key binding | Description | + |-------------+----------------------------------------------------| + | ~SPC m c :~ | Run a custom compile command with =elixirc= | + | ~SPC m c b~ | Compile the current buffer with elixirc. =elixirc= | + | ~SPC m c f~ | Compile the given filename with =elixirc= | -** Execute +*** Execute -| Key binding | Description | -|-------------+--------------------------------------------| -| ~SPC m x :~ | Run a custom execute command with =elixir= | -| ~SPC m x b~ | Run the current buffer through =elixir= | -| ~SPC m x f~ | Run =elixir= with the given filename | + | Key binding | Description | + |-------------+--------------------------------------------| + | ~SPC m x :~ | Run a custom execute command with =elixir= | + | ~SPC m x b~ | Run the current buffer through =elixir= | + | ~SPC m x f~ | Run =elixir= with the given filename | -** Code Definition Jump +*** Code Definition Jump -| Key binding | Description | -|-------------+----------------------------------------------------| -| ~SPC m g g~ | Jump to the elixir expression definition at point. | -| ~SPC m .~ | Jump to the elixir expression definition at point. | -| ~SPC m g b~ | Pop back to where ~SPC m g g~ was last invoked. | -| ~SPC m ,~ | Pop back to where ~SPC m g g~ was last invoked. | -| ~SPC m g n~ | Jump to next symbol definition | -| ~SPC m g N~ | Jump to previous symbol definition | -| ~SPC m g j~ | Choose which symbol definition to jump to | + | Key binding | Description | + |-------------+----------------------------------------------------| + | ~SPC m g g~ | Jump to the elixir expression definition at point. | + | ~SPC m .~ | Jump to the elixir expression definition at point. | + | ~SPC m g b~ | Pop back to where ~SPC m g g~ was last invoked. | + | ~SPC m ,~ | Pop back to where ~SPC m g g~ was last invoked. | + | ~SPC m g n~ | Jump to next symbol definition | + | ~SPC m g N~ | Jump to previous symbol definition | + | ~SPC m g j~ | Choose which symbol definition to jump to | -** Hex (packages) -Hex is the package manager for Elixir & Erlang ecosystem. See [[https://hex.pm]]. +*** Hex (packages) + Hex is the package manager for Elixir & Erlang ecosystem. See [[https://hex.pm]]. -| Key binding | Description | -|-------------+----------------------------------------------------------| -| ~SPC m X i~ | Display Hex package information for the package at point | -| ~SPC m X r~ | Display Hex package releases for the package at point | -| ~SPC m X R~ | Display Hex package releases for a certain package | -| ~SPC m X I~ | Display Hex package info for a certain package | -| ~SPC m X s~ | Search for Hex packages | + | Key binding | Description | + |-------------+----------------------------------------------------------| + | ~SPC m X i~ | Display Hex package information for the package at point | + | ~SPC m X r~ | Display Hex package releases for the package at point | + | ~SPC m X R~ | Display Hex package releases for a certain package | + | ~SPC m X I~ | Display Hex package info for a certain package | + | ~SPC m X s~ | Search for Hex packages | -** Macro expand +*** Macro expand -| Key binding | Description | -|-------------+-----------------------------------------------------------------------------------| -| ~SPC m o l~ | Macro expand once the Elixir code on the current line | -| ~SPC m o L~ | Macro expand once the Elixir code on the current line and insert the result | -| ~SPC m o k~ | Macro expand completely the Elixir code on the current line | -| ~SPC m o K~ | Macro expand completely the Elixir code on the current line and insert the result | -| ~SPC m o i~ | Macro expand once the Elixir code on marked region | -| ~SPC m o I~ | Macro expand once the Elixir code on marked region once and insert the result | -| ~SPC m o r~ | Macro expand completely the Elixir code on marked region | -| ~SPC m o R~ | Macro expand completely the Elixir code on marked region and insert the result | + | Key binding | Description | + |-------------+-----------------------------------------------------------------------------------| + | ~SPC m o l~ | Macro expand once the Elixir code on the current line | + | ~SPC m o L~ | Macro expand once the Elixir code on the current line and insert the result | + | ~SPC m o k~ | Macro expand completely the Elixir code on the current line | + | ~SPC m o K~ | Macro expand completely the Elixir code on the current line and insert the result | + | ~SPC m o i~ | Macro expand once the Elixir code on marked region | + | ~SPC m o I~ | Macro expand once the Elixir code on marked region once and insert the result | + | ~SPC m o r~ | Macro expand completely the Elixir code on marked region | + | ~SPC m o R~ | Macro expand completely the Elixir code on marked region and insert the result | -** Formatting +*** Formatting -| Key binding | Description | -|-------------+---------------------------| -| ~SPC m =~ | Format the current buffer | + | Key binding | Description | + |-------------+---------------------------| + | ~SPC m =~ | Format the current buffer | +** LSP + You find and overview of all the key bindings on the [[https://github.com/syl20bnr/spacemacs/tree/develop/layers/%2Btools/lsp#key-bindings][lsp layer description]]. diff --git a/layers/+lang/elixir/config.el b/layers/+lang/elixir/config.el index b4e41aef6..c43ecf332 100644 --- a/layers/+lang/elixir/config.el +++ b/layers/+lang/elixir/config.el @@ -17,4 +17,10 @@ Default is nil because Elixir compilation is based on macros and thus it is unsafe. Activate this option only for trusted code, usage of a directory variable is recommended.") +(defvar elixir-backend 'alchemist + "The backend to use for IDE features. Possible values are `alchemist' and `lsp'.") + +(defvar elixir-ls-path "~/elixir-ls/release" + "The path to the folder that contains the elixir-ls release, start scripts (language_server.sh/language_server.bat).") + (spacemacs|define-jump-handlers elixir-mode) diff --git a/layers/+lang/elixir/funcs.el b/layers/+lang/elixir/funcs.el index 4d8460fc4..4327f85ad 100644 --- a/layers/+lang/elixir/funcs.el +++ b/layers/+lang/elixir/funcs.el @@ -9,6 +9,62 @@ ;; ;;; License: GPLv3 +(defun spacemacs//elixir-setup-backend () + "Conditionally setup elixir backend." + (pcase elixir-backend + (`alchemist (spacemacs//elixir-setup-alchemist)) + (`lsp (spacemacs//elixir-setup-lsp)))) + +(defun spacemacs//elixir-setup-company () + "Conditionally setup company based on backend." + (if (eq elixir-backend `alchemist) + (spacemacs//elixir-setup-alchemist-company) + (spacemacs//elixir-setup-lsp-company))) + + +;;alchemist + +(defun spacemacs//elixir-setup-alchemist () + (alchemist-mode)) + +(defun spacemacs//elixir-setup-alchemist-company () + (when (configuration-layer/package-used-p 'alchemist) + (progn + (spacemacs|add-company-backends + :backends alchemist-company + :modes elixir-mode alchemist-iex-mode) + (company-mode)))) + + +;;lsp + +(defun spacemacs//elixir-setup-lsp () + "Setup lsp backend." + (if (configuration-layer/layer-used-p 'lsp) + (progn (add-to-list 'exec-path elixir-ls-path) (lsp)) + (message "`lsp' layer is not installed, please add `lsp' layer to your dotfile.")) + (if (configuration-layer/layer-used-p 'dap) + (progn + (require 'dap-elixir) + (spacemacs/set-leader-keys-for-major-mode 'elixir-mode "db" nil) + (spacemacs/dap-bind-keys-for-mode 'elixir-mode)) + (message "`dap' layer is not installed, please add `dap' layer to your dotfile."))) + +(defun spacemacs//elixir-setup-lsp-company () + "Setup lsp auto-completion." + (if (configuration-layer/layer-used-p 'lsp) + (progn + (spacemacs|add-company-backends + :backends company-lsp + :modes elixir-mode + :append-hooks nil + :call-hooks t) + (company-mode)) + (message "`lsp' layer is not installed, please add `lsp' layer to your dotfile."))) + + +;; others + (defun spacemacs//elixir-looking-back-special-p (expr) (save-excursion (when (or (looking-back " ") diff --git a/layers/+lang/elixir/packages.el b/layers/+lang/elixir/packages.el index f6bc65223..be1c330cf 100644 --- a/layers/+lang/elixir/packages.el +++ b/layers/+lang/elixir/packages.el @@ -26,13 +26,12 @@ )) (defun elixir/post-init-company () - (when (configuration-layer/package-used-p 'alchemist) - (spacemacs|add-company-backends - :backends alchemist-company - :modes elixir-mode alchemist-iex-mode))) + ;; backend specific + (add-hook 'elixir-mode-local-vars-hook #'spacemacs//elixir-setup-company)) (defun elixir/init-alchemist () (use-package alchemist + :if (eq elixir-backend 'alchemist) :defer t :init (progn @@ -165,6 +164,8 @@ (defun elixir/init-elixir-mode () (use-package elixir-mode :defer t + :init (spacemacs/add-to-hook 'elixir-mode-hook + '(spacemacs//elixir-setup-backend)) :config (spacemacs/set-leader-keys-for-major-mode 'elixir-mode "=" 'elixir-format)))