This configuration file contains setup of Emacs packages for Language Server Protocol (LSP). I will use use-package
for package management.
Since this is an org file, I am using org-babel-load-file
command to load this file from init.el file. Emacs loads init.el when it starts. I have setup a variable called EMACS_DIR
to point to .emacs.d as the setup directory. Adjust it to match your system. To speed up loading time of emacs, I have used gc-cons-threshold
and gc-cons-percentage
variables, before and after loading this configuration file. I have set gc-cons-threshold
value to 300 mb after startup. Adjust it to comfortable value according to memory in your system. lsp-java
package however has 1GB
setup as default.
In the following code block, we will initialize package repositories and after that install use-package
. This package is used to install other packages.
(require 'package)
(setq package-archives '(("melpa" . "https://melpa.org/packages/")
("elpa" . "https://elpa.gnu.org/packages/")
("org" . "https://orgmode.org/elpa/")
))
(package-initialize)
; Fetch the list of packages available
(unless package-archive-contents (package-refresh-contents))
; Install use-package
(setq package-list '(use-package))
(dolist (package package-list)
(unless (package-installed-p package) (package-install package)))
In some operating systems Emacs does not load environment variables properly. Therefore, below we install a package called exec-path-from-shell
and initialize it.
(use-package exec-path-from-shell :ensure t)
(exec-path-from-shell-initialize)
The extensible vi layer for Emacs.
;; Download Evil
(unless (package-installed-p 'evil)
(package-install 'evil))
;; Enable Evil
(require 'evil)
(evil-mode 1)
;;(load (expand-file-name "~/.quicklisp/slime-helper.el"))
;;(setq inferior-lisp-program "/usr/local/bin/sbcl")
;; (stumpwm:define-key stumpwm:*root-map* (stumpwm:kbd "C-z") "echo Zzzzz...")
;;
PDF Tools is, among other things, a replacement of Emacs’s DocView for PDF
;; pdf support
;;(load "pdf-tools-init.el")
Angular Support for Emacs
(with-eval-after-load 'typescript-mode (add-hook 'typescript-mode-hook #'lsp))
Emacs major mode for scripting with the ion-shell.
(use-package ion-mode
:straight (ion-mode
:host github :repo "iwahbe/ion-mode")
:mode (("\\.ion\\'" . ion-mode)
("/ion/initrc\\'" . ion-mode))
)
Here, I add some basic emacs setup like loading the language, disabling the toolbar, setting up backup directory etc. I have added comments to each setting.
;; Disable annoying ring-bell when backspace key is pressed in certain situations
(setq ring-bell-function 'ignore)
;; Disable scrollbar and toolbar
(scroll-bar-mode -1)
(tool-bar-mode -1)
;; Set language environment to UTF-8
(set-language-environment "UTF-8")
(set-default-coding-systems 'utf-8)
;; Longer whitespace, otherwise syntax highlighting is limited to default column
(setq whitespace-line-column 1000)
;; Enable soft-wrap
(global-visual-line-mode 1)
;; Maintain a list of recent files opened
(recentf-mode 1)
(setq recentf-max-saved-items 50)
;; Move all the backup files to specific cache directory
;; This way you won't have annoying temporary files starting with ~(tilde) in each directory
;; Following setting will move temporary files to specific folders inside cache directory in EMACS_DIR
(setq user-cache-directory (concat EMACS_DIR "cache"))
(setq backup-directory-alist `(("." . ,(expand-file-name "backups" user-cache-directory)))
url-history-file (expand-file-name "url/history" user-cache-directory)
auto-save-list-file-prefix (expand-file-name "auto-save-list/.saves-" user-cache-directory)
projectile-known-projects-file (expand-file-name "projectile-bookmarks.eld" user-cache-directory))
;; Org-mode issue with src block not expanding
;; This is a fix for bug in org-mode where <s TAB does not expand SRC block
(when (version<= "9.2" (org-version))
(require 'org-tempo))
;; Coding specific setting
;; Automatically add ending brackets and braces
(electric-pair-mode 1)
;; Make sure tab-width is 4 and not 8
(setq-default tab-width 4)
;; Highlight matching brackets and braces
(show-paren-mode 1)
I tend to like doom-themes package. Below we will install doom theme. In addition, I will also install a package called heaven-and-hell. This allows us to toggle between two themes using a shortcut key. I will assign F6
key to toggling the theme and C-c F6
to set to default theme.
(use-package doom-themes
:ensure t
:init
(load-theme 'doom-palenight t))
(use-package heaven-and-hell
:ensure t
:init
(setq heaven-and-hell-theme-type 'dark)
(setq heaven-and-hell-themes
'((light . doom-acario-light)
(dark . doom-palenight)))
:hook (after-init . heaven-and-hell-init-hook)
:bind (("C-c <f6>" . heaven-and-hell-load-default-theme)
("<f6>" . heaven-and-hell-toggle-theme)))
If you press F6 key in your keyboard, it should switch between doom-palenight and doom-acario-light themes. If you want to go back to the default theme press Ctrl + C and F6
.
This will help eliminate weird escape sequences during compilation of projects.
(defun my/ansi-colorize-buffer ()
(let ((buffer-read-only nil))
(ansi-color-apply-on-region (point-min) (point-max))))
(use-package ansi-color
:ensure t
:config
(add-hook 'compilation-filter-hook 'my/ansi-colorize-buffer)
)
In this section we will install some of the packages that we will use for various project and file management.
Key-Chord allows us to bind regular keyboard keys for various commands without having to use prefix keys such as Ctrl, Alt or Super etc.
(use-package use-package-chords
:ensure t
:init
:config (key-chord-mode 1)
(setq key-chord-two-keys-delay 0.4)
(setq key-chord-one-key-delay 0.5) ; default 0.2
)
Here, we changed the delay for the consecutive key to be little higher than default. Adjust this to what you feel comfortable.
Projectile helps us with easy navigation within a project. Projectile recognizes several source control managed folders e.g git, mercurial, maven, sbt, and a folder with empty .projectile file. You can use C-c p
to invoke any projectile command. This is a very useful key to remember.
(use-package projectile
:ensure t
:init (projectile-mode +1)
:config
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
)
Helm allows for easy completion of commands. Below, we will replace several of the built in functions with helm versions and add keyboard shortcuts for couple of new useful commands.
(use-package helm
:ensure t
:init
(helm-mode 1)
(progn (setq helm-buffers-fuzzy-matching t))
:bind
(("C-c h" . helm-command-prefix))
(("M-x" . helm-M-x))
(("C-x C-f" . helm-find-files))
(("C-x b" . helm-buffers-list))
(("C-c b" . helm-bookmarks))
(("C-c f" . helm-recentf)) ;; Add new key to recentf
(("C-c g" . helm-grep-do-git-grep))) ;; Search using grep in a git project
I want to point out, couple of interesting things from above setup. Just like we added C-c p
as a prefix for projectile, here we added C-c h
for helm. We also enabled fuzzy matching, so that your search text don’t need to be very strict. Also, I added C-c g
to helm-grep-do-git-grep. I can search files with specific text within a git project (make sure to commit it first).
Helm descbinds helps to easily search for keyboard shortcuts for modes that are currently active in the project. This can be helpful to discover keyboard shortcuts to various commands. Use C-h b
to bring up helm-descbinds window.
(use-package helm-descbinds
:ensure t
:bind ("C-h b" . helm-descbinds))
E.g. In helm-descbinds window you could type “helm” and “projectile” and see all the shortcuts assigned to various commands.
Helm swoop allows to quickly search for text under cursor or new text within current file. I am sure you are already using C-s
and C-r
to search within the file. This package compliments rather than replace it. You can quickly type js
to search and jump to the target line. To go back to where you started searching, use jp
. You can use M-m
from C-s
and C-r
search to start using helm-swoop as described in below setting.
(use-package helm-swoop
:ensure t
:chords
("js" . helm-swoop)
("jp" . helm-swoop-back-to-last-point)
:init
(bind-key "M-m" 'helm-swoop-from-isearch isearch-mode-map)
;; If you prefer fuzzy matching
(setq helm-swoop-use-fuzzy-match t)
;; Save buffer when helm-multi-swoop-edit complete
(setq helm-multi-swoop-edit-save t)
;; If this value is t, split window inside the current window
(setq helm-swoop-split-with-multiple-windows nil)
;; Split direction. 'split-window-vertically or 'split-window-horizontally
(setq helm-swoop-split-direction 'split-window-vertically)
;; If nil, you can slightly boost invoke speed in exchange for text color
(setq helm-swoop-speed-or-color nil)
;; ;; Go to the opposite side of line from the end or beginning of line
(setq helm-swoop-move-to-line-cycle t)
)
Avy allows you to quickly jump to certain character, word or line within the file. Use jc
, jw
or jl
to quickly jump within current file. Change it to other keys, if you feel you are using this set of keys for other purposes.
(use-package avy
:ensure t
:chords
("jc" . avy-goto-char)
("jw" . avy-goto-word-1)
("jl" . avy-goto-line))
For some prefix commands like C-c p
or C-c h
we want Emacs to visually guide you through the available options. Following package allows us to do that.
(use-package which-key
:ensure t
:init
(which-key-mode)
)
We can use quickrun package to execute code (if it has main). E.g. If you have a java file with main method, it will run with the associated shortcut key C-c r
or quickrun command. Quickrun has support for several languages.
(use-package quickrun
:ensure t
:bind ("C-c r" . quickrun))
With above setup done, below we will setup several packages closely related to LSP.
Complete anything aka Company provides auto-completion. Company-capf is enabled by default when you start LSP on a project. You can also invoke M-x company-capf
to enable capf (completion at point function).
(use-package company :ensure t)
Yasnippet is a template system for Emacs. It allows you to type abbreviation and complete the associated text.
(use-package yasnippet :config (yas-global-mode))
(use-package yasnippet-snippets :ensure t)
E.g. In java mode, if you type pr
and hit <TAB>
it should complete to System.out.println("text");
To create a new snippet you can use yas-new-snippet
command.
FlyCheck checks for errors in code at run-time.
(use-package flycheck :ensure t :init (global-flycheck-mode))
Emacs Debug Adapter Protocol aka DAP Mode allows us to debug your program. Below we will integrate dap-mode
with dap-hydra
. Dap-hydra
shows keys you can use to enable various options and jump through code at runtime. After we install dap-mode we will also install dap-java
.
(use-package dap-mode
:ensure t
:after (lsp-mode)
:functions dap-hydra/nil
:config
(require 'dap-java)
:bind (:map lsp-mode-map
("<f5>" . dap-debug)
("M-<f5>" . dap-hydra))
:hook ((dap-mode . dap-ui-mode)
(dap-session-created . (lambda (&_rest) (dap-hydra)))
(dap-terminated . (lambda (&_rest) (dap-hydra/nil)))))
(use-package dap-java :ensure nil)
Treemacs provides UI elements used for LSP UI. Let’s install lsp-treemacs and its dependency treemacs. We will also Assign M-9
to show error list.
(use-package lsp-treemacs
:after (lsp-mode treemacs)
:ensure t
:commands lsp-treemacs-errors-list
:bind (:map lsp-mode-map
("M-9" . lsp-treemacs-errors-list)))
(use-package treemacs
:ensure t
:commands (treemacs)
:after (lsp-mode))
LSP UI is used in various packages that require UI elements in LSP. E.g. lsp-ui-flycheck-list
opens a windows where you can see various coding errors while you code. You can use C-c l T
to toggle several UI elements. We have also remapped some of the xref-find functions, so that we can easily jump around between symbols using M-.
, M-,
and M-?
keys.
(use-package lsp-ui
:ensure t
:after (lsp-mode)
:bind (:map lsp-ui-mode-map
([remap xref-find-definitions] . lsp-ui-peek-find-definitions)
([remap xref-find-references] . lsp-ui-peek-find-references))
:init (setq lsp-ui-doc-delay 1.5
lsp-ui-doc-position 'bottom
lsp-ui-doc-max-width 100
))
Go through this link to see what other parameters are provided.
Helm-lsp provides various functionality to work with the code. E.g. Code actions like adding getter, setter, toString, refactoring etc. You can use helm-lsp-workspace-symbol
to find various symbols (classes) within your workspace.
LSP’s built in symbol explorer uses xref-find-apropos
to provide symbol navigation. Below we will replace that with helm version. After that you can use C-c l g a
to find workspace symbols in a more intuitive way.
(use-package helm-lsp
:ensure t
:after (lsp-mode)
:commands (helm-lsp-workspace-symbol)
:init (define-key lsp-mode-map [remap xref-find-apropos] #'helm-lsp-workspace-symbol))
Let’s install the main package for lsp. Here we will integrate lsp with which-key. This way, when we type the prefix key C-c l
we get additional help for completing the command.
(use-package lsp-mode
:ensure t
:hook (
(lsp-mode . lsp-enable-which-key-integration)
(java-mode . #'lsp-deferred)
)
:init (setq
lsp-keymap-prefix "C-c l" ; this is for which-key integration documentation, need to use lsp-mode-map
lsp-enable-file-watchers nil
read-process-output-max (* 1024 1024) ; 1 mb
lsp-completion-provider :capf
lsp-idle-delay 0.500
)
:config
(setq lsp-intelephense-multi-root nil) ; don't scan unnecessary projects
(with-eval-after-load 'lsp-intelephense
(setf (lsp--client-multi-root (gethash 'iph lsp-clients)) nil))
(define-key lsp-mode-map (kbd "C-c l") lsp-command-map)
)
You can start LSP server in a java project by using C-c l s s
. Once you type C-c l
which-key
package should guide you through rest of the options. In above setting I have added some memory management settings as suggested in this guide. Change them to higher numbers, if you find lsp-mode sluggish in your computer.
This is the package that handles server installation and session management.
(use-package lsp-java
:ensure t
:config (add-hook 'java-mode-hook 'lsp))
Go through Supported commands section of lsp-java github page to see commands provided in lsp-mode. Most of these commands are available under lsp’s C-c l
option. I hope this configuration file was useful.