I’ve got elixir-mode
and lsp-mode
(before I was using eglot
but the same problem persisted) configured in my emacs.d and it mostly works. One thing I can’t figure out though is how to work with the code completion (via company) in elixir code.
Say I type defm
at this point I get the completion options pop up. I select defmodule
with enter
and then tab
to expand. The code expands to:
defmodule $1 do
$0
end
However the cursor (point) moves to the end of end
. I get no opportunity to specify the module name and also complete the module body.
What I was expecting was, upon pressing tab, the code will expand as it does now but the cursor (point) should be placed at $1
where I can type the module name, then another tab
will move cursor (point) to $0
for the content of the module body in that order.
When I disable lsp-mode
and use only elixir-mode
I get the expected behaviour were selecting defmodule
from the completion list places the cursor (point) between defmodule
and ``do where I get to type the module name then another tab places the cursor inside the module body.
How can I get the experience I just described with lsp-mode
and elixir? I have included below a stripped down version of my config in case someone will like to try is recreate what I've described.
;; Produce backtraces when errors occur
;;(setq debug-on-error t)
(setq message-log-max 10000)
;; Add aditional load paths
;;(add-to-list 'load-path (expand-file-name "lisp" user-emacs-directory))
;;(add-to-list 'load-path (expand-file-name "site-lisp" user-emacs-directory))
(defconst my-savefile-dir (expand-file-name "savefile" user-emacs-directory))
;; create the savefile dir if it doesn't exist
(unless (file-exists-p my-savefile-dir)
(make-directory my-savefile-dir))
(defconst *is-a-mac* (eq system-type 'darwin))
;;----------------------------------------------------------------------------
;; Adjust garbage collection thresholds during startup, and thereafter
;;----------------------------------------------------------------------------
(let ((normal-gc-cons-threshold (* 20 1024 1024))
(init-gc-cons-threshold (* 128 1024 1024)))
(setq gc-cons-threshold init-gc-cons-threshold)
(add-hook 'emacs-startup-hook
(lambda () (setq gc-cons-threshold normal-gc-cons-threshold))))
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
;; Package configs
(require 'package)
(setq package-enable-at-startup nil)
(setq package-archives '(("org" . "http://orgmode.org/elpa/")
("gnu" . "http://elpa.gnu.org/packages/")
("melpa" . "https://melpa.org/packages/")))
(package-initialize)
;; bootstrap `use-package`
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
;; From use-package README
(eval-when-compile
(require 'use-package))
;; To aid benchmarking
(setq use-package-verbose t)
(unless (fboundp 'helm-mode)
(ido-mode t)
(setq ido-enable-flex-matching t))
(autoload 'zap-up-to-char "misc"
"Kill up to, but not including ARGth occurrence of CHAR." t)
(require 'uniquify)
(setq uniquify-buffer-name-style 'forward)
(require 'saveplace)
(setq-default save-place t)
(global-set-key (kbd "M-/") 'hippie-expand)
(global-set-key (kbd "C-x C-b") 'ibuffer)
(global-set-key (kbd "M-z") 'zap-up-to-char)
(global-set-key (kbd "C-s") 'isearch-forward-regexp)
(global-set-key (kbd "C-r") 'isearch-backward-regexp)
(global-set-key (kbd "C-M-s") 'isearch-forward)
(global-set-key (kbd "C-M-r") 'isearch-backward)
(show-paren-mode 1)
(setq-default indent-tabs-mode nil)
(setq save-interprogram-paste-before-kill t
apropos-do-all t
mouse-yank-at-point t
require-final-newline t
visible-bell t
load-prefer-newer t
ediff-window-setup-function 'ediff-setup-windows-plain
save-place-file (concat user-emacs-directory "places")
backup-directory-alist `(("." . ,(concat user-emacs-directory
"backups"))))
(use-package exec-path-from-shell
:if (memq window-system '(mac ns))
:ensure t
:init (setq-default exec-path-from-shell-arguments nil)
:config
(progn
(dolist (var '("SSH_AUTH_SOCK" "SSH_AGENT_PID" "GPG_AGENT_INFO" "LANG" "LC_CTYPE" "WORKON_HOME"))
(add-to-list 'exec-path-from-shell-variables var))
(exec-path-from-shell-initialize)))
;; Flycheck
(use-package flycheck
:ensure t
:init
(setq flycheck-display-errors-function #'flycheck-display-error-messages-unless-error-list)
:init (global-flycheck-mode))
;; Company
(use-package company
:ensure t
:hook (after-init . global-company-mode)
:init
(setq tab-always-indent 'complete)
(setq company-minimum-prefix-length 2)
(setq company-dabbrev-downcase 0)
(setq company-idle-delay 0)
(setq company-require-match 'never)
(setq company-show-numbers t)
(setq-default company-dabbrev-other-buffers 'all
company-tooltip-align-annotations t)
(setq tab-always-indent 'complete)
:config
(define-key company-mode-map (kbd "M-/") 'company-complete)
(define-key company-active-map (kbd "C-n") 'company-select-next)
(define-key company-active-map (kbd "C-p") 'company-select-previous))
;; Suspend page-break-lines-mode while company menu is active
;; (see https://github.com/company-mode/company-mode/issues/416)
(with-eval-after-load 'company
(with-eval-after-load 'page-break-lines
(defvar-local sanityinc/page-break-lines-on-p nil)
(defun sanityinc/page-break-lines-disable (&rest ignore)
(when (setq sanityinc/page-break-lines-on-p (bound-and-true-p page-break-lines-mode))
(page-break-lines-mode -1)))
(defun sanityinc/page-break-lines-maybe-reenable (&rest ignore)
(when sanityinc/page-break-lines-on-p
(page-break-lines-mode 1)))
(add-hook 'company-completion-started-hook 'sanityinc/page-break-lines-disable)
(add-hook 'company-after-completion-hook 'sanityinc/page-break-lines-maybe-reenable)))
;; LSP
(use-package lsp-mode
:ensure t
:commands lsp
:hook ((elixir-mode . lsp))
:init
(setq lsp-prefer-flymake nil)
(setq lsp-enable-completion-at-point t)
(setq lsp-enable-xref t)
:config
(add-to-list 'exec-path "/Users/napo/github/elixir-ls/release")) ;; Uncomment for lsp-mode
(use-package lsp-ui
:after (lsp-mode)
:hook (lsp-mode . lsp-ui-mode)
:ensure t
:init
;;(setq lsp-ui-sideline-enable nil)
;;(setq lsp-ui-sideline-ignore-duplicate t)
(setq lsp-ui-doc-max-width 50)
(setq lsp-ui-doc-max-hight 20)
(setq lsp-ui-doc-include-signature t)
(setq lsp-ui-peek-always-show t)
:commands lsp-ui-mode)
(use-package company-lsp
:after (company lsp-mode)
:ensure t
:commands company-lsp
:init
(push 'company-lsp company-backends))
;; Elxir
(use-package elixir-mode
:after (company lsp-mode)
:ensure t
:config
;; Create a buffer-local hook to run elixir-format on save, only when we enable elixir-mode.
(add-hook 'elixir-mode-hook
(lambda () (add-hook 'before-save-hook 'elixir-format nil t))))
Thanks.