Emacs is a free software text editor, extensible via the programming language Emacs Lisp
In this article, we will see how to add the following functionnalities to Emacs with custom Elisp (Emacs Lisp) configuration
- Advanced syntaxic coloration with tree-sitter
- Live code analysis: LSP Mode
- Integrated debugger: DAP Mode
Installing a first package
Each software cited previously in the functionnalities we will add is distributed in the form of Emacs Lisp packages. It is similar to Debian packages: there are package requirements, you download and install a tarball of Emacs Lisp code.
The first package we are going to install is dap-mode
Configuration of Melpa package repository
The dap-mode
package is distributed on Melpa
package repository. In order to add Melpa to Emacs known repositories,
we need to add a bit of Elisp code to the file ~/.emacs.d/init.el
~/.emacs.d/init.el
is the entrypoint (i.e main) configuration file
of Emacs. It is executed by default when the Emacs text editor is
launched. It contains Emacs Lisp code (.el
extension).
;; `package` feature is part of Emacs builtin packages
;; This feature will be useful to install other packages like `dap-mode`
(require 'package)
;; Add Melpa to the list of Emacs package repositories
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
;; Load Emacs Lisp packages, and activate them
(package-initialize)
Listing available packages in Melpa
Adding a package repository to the list package-archives
has not
the side effect to fetch the list of packages of that repository
In order to fetch the list of packages of Melpa, we need to call the
functionpackage-refresh-contents
of package
. Its like calling apt update
on Emacs
We will automate the following logic on Emacs startup: if the list of known packages is empty, then we will fetch the packages available on all repos. That way we doesn’t bloat the init script with network call if we already a list of available packages
;; package-archive-contents variable
;; holds the list of known packages
(unless package-archive-contents (package-refresh-contents))
One of the cool functionnalities of Emacs is that the text Editor is
its builtin documentaion. You can describe any Elisp
function/variable available with describe-function
(keyboard shortcut C-h
f
i.e
press Control+h
then press f
) and describe-variable
(keyboard shortcut C-h
v
)
To launch an interactive Emacs Lisp function, use keyboard shortcut
Alt+x
(i.e M-x
) and then enter the Elisp command. For example, do
M-x
describe-function
Installing dap-mode
Now that we have an up-to-date packages list, we can install package dap-mode
;; Let's define the list of required package in a new variable: package-list
(setq package-list '(dap-mode))
;; If a package of package-list is not installed, install it
(dolist (package package-list)
(unless (package-installed-p package) (package-install package)))
You also can manually browse packages with M-x
package-list-packages
and install them with M-x
package-install
.
Typescript configuration
Typescript Major Mode
Emacs allow a single mode d’édition majeur for each buffer (~= open file), and many minor edition modes
Let’s install a major mode for Typescript:
;; Let's define the list of required package in a new variable: package-list
(setq package-list '(dap-mode typescript-mode))
All Typescript files (ending with .ts
) will now be opened with
Typescript mode.
At this point, opened Typescript files will have appropriate syntaxic
coloration, but we can improve it furthermore with tree-sitter
package
Tree-Sitter advanced syntaxic coloration
Let’s install tree-sitter package. Is allow us to color programming languages keywords with more precision, because it constructs an abstract syntax tree of the language, and them give each node an appropriated color. This give us more precise colors than Typescript major mode can offer natively
;; Let's define the list of required package in a new variable: package-list
(setq package-list '(dap-mode typescript-mode tree-sitter tree-sitter-langs))
;; Loading tree-sitter package
(require 'tree-sitter-langs)
(require 'tree-sitter)
;; Activate tree-sitter globally (minor mode registered on every buffer)
(global-tree-sitter-mode)
(add-hook 'tree-sitter-after-on-hook #'tree-sitter-hl-mode)
If you prefer Emacs theme on the right, you can enable it with
(load-theme 'tango-dark)
LSP Mode
LSP mode provides code actions and hints like:
- Variable types
- Goto definition
- Rename variable/function everywhere in a project
- Detect and highlight errors
- …and most of the functionnalities an IDE provides
;; Let's define the list of required package in a new variable: package-list
(setq package-list '(dap-mode typescript-mode tree-sitter tree-sitter-langs lsp-mode lsp-ui))
(require 'lsp-mode)
(add-hook 'typescript-mode-hook 'lsp-deferred)
(add-hook 'javascript-mode-hook 'lsp-deferred)
DAP Mode
We will now provide our text editor a Debugger using DAP Mode, and use it on Typescript project
Installing DAP Mode
dap-node
feature from dap-mode
package contains code specific to
NodeJS projects debugging. We can load these features (i.e set of
functions/variables) using (require 'dap-node)
when we are visiting
a Typescript file
We can achieve that (load dap-node
feature in Typescript buffer
only) using Emacs hooks
Using Emacs NodeJS debugger with dap-mode
requires downloading and
install of a VSCode module with dap-node-setup
. We will also
automate that:
(defun my-setup-dap-node ()
"Require dap-node feature and run dap-node-setup if VSCode module isn't already installed"
(require 'dap-node)
(unless (file-exists-p dap-node-debug-path) (dap-node-setup)))
(add-hook 'typescript-mode-hook 'my-setup-dap-node)
(add-hook 'javascript-mode-hook 'my-setup-dap-node)
Verify that installation worked out
A recuring bug during dap-node-setup
is that Microsoft server
hosting this tarball respond with 429
HTTP error code (request rate
limit exceeded)
In order to check that the install succeedeed, you can launch this lisp script:
(require 'dap-node)
(if (file-exists-p (nth 1 dap-node-debug-program))
(message "NodeJS debugger successfully installed")
(message "NodeJS debugger install failed. Please download it manually"))
To launch this script, copy it in an Emacs buffer (*scratch* for
example), highlight it and launch command eval-region
Manual install procedure for NodeJS debug adapter
To install manually the driver if the server refused the download on dap-node-setup
:
- Go to the VSCode Node Debug page
- Download the extension
- Then unzip the tarball to the
~/.emacs.d/.extension/vscode/
directory
You can use Emacs
shell
to achieve that. Run M-x
eshell
, and an Emacs Shell will
be open. Eshell allow running both Elisp code and shell commands That
way, you can evaluate the destination of the archive with (eval dap-node-debug-path)
and use unzip shell command at the same time
Using Typescript Debugger
Node debugger installed by dap-node-setup
and Emacs client
dap-mode
now allow us to debug a Typescript project
Create a directory and open an index.ts
file inside it. Install
tsc
with npm i typescript
LSP Mode is going to ask you which language server you want to
install. Select ts-ls
Then, LSP Mode will ask you to save this project in the list of known
projects. Accept with i
You can now write Typescript code in your index.ts
file with hints
and advanced syntax highlighting
You can also debug this file. I advise you to run command dap-hydra
which provides fast shortcuts for debugging
Once dap-hydra
command launched, you can add a breakpoint on the
first line of code with b
a
Then, evaluate the following Elisp code:
(dap-register-debug-template
"Launch index.ts"
(list :type "node"
:request "launch"
:program "${workspaceFolder}/index.ts"
:dap-compilation "npx tsc index.ts --outdir dist --sourceMap true"
:outFiles (list "${workspaceFolder}/dist/**/*.js")
:name "Launch index.ts"))
And run d
d
to create a debug session.
Here is what we obtain:
How to configure the debugger: dap-register-debug-template
To run Emacs debugger, you have to configure a debug template. We will explain the previously added debug template for NodeJS:
(dap-register-debug-template
"Launch index.ts"
(list :type "node"
:request "launch"
:program "${workspaceFolder}/index.ts"
:dap-compilation "npx tsc index.ts --outdir dist --sourceMap true"
:outFiles (list "${workspaceFolder}/dist/**/*.js")
:name "Launch index.ts"))
First argument of dap-register-debug-template
is the name of the
debug template that will be prompted when you ask to run a debug session.
Second arguments is a list of configuration symbols (like :type
) and
associated values (like "node"
)
Here, the template named Launch index.ts
will use NodeJs debugger
(:type
) to launch (:request
) a debug session on :program
file
index.ts
. ${workspaceFolder}
is a special string replaced with the
project workspace folder by dap-mode
runtime
Before launching the debug session, the command npx tsc index.ts --outdir dist --sourceMap true
will run (to compile the Typescript
code to Javascript code so NodeJS can run it)
:outFiles
variable specifies a wildcard where the Javascript files
corresponding to the Typescript files can be found.
See dap-mode
NodeJS
documentation
for more
Conclusion
I hope this tutorial allowed you to quickstart with Emacs text editor configured for Typescript code editing, as well as understanding the Emacs Lisp basics
In order to continue customizing Emacs, here is a list of awesome Emacs packages sorted by themes
Also, don’t skip the official Elisp tutorial
Elisp,
and use C-h
f
/C-h
v
commands to describe the features
available in Emacs