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

Emacs 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)

Describe Emacs function

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)

Emacs default and tango-dark themes

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

Utilisation de Eshell pour installer l’extension

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:

Debug Typescript avec Emacs

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