Using Emacs for your Typescript/Node project

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

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 major mode 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. It 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)

Abonne-toi !

On te partage nos meilleurs conseils et découvertes sur Python et PostgreSQL toutes les deux semaines

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

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:

You can the Emacs shell 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

On va utiliser le projet d’affichage de flux rss et le déboguer avec Emacs


Create a directory and open an index.ts file inside it. Install tsc with npm i typescript

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

Using launch.json

You can define your project debug configurations in launch.json .vscode/launch.json. Here is an example:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Launch index",
      "program": "${workspaceFolder}/index.ts",
      "preLaunchTask": "npm: build",
      "dap-compilation": "npm run build",
      "outFiles": ["${workspaceFolder}/dist/**/*.js"]
    }
  ]
}

This file describe the debug configuration. It is named Launch index. This config will automatically be loaded by dap-mode if it is in the right directory relatively to the project

Here is an explanation of the config: Node can't directly interpret Typescript code. We have to compile Typescript to Javascript first. It is done before running the debugger, using dap-compilation Emacs. It is the equivalent of preLaunchTask in VSCode

In this config, the npm script build will launch tsc and output into dist

In order for the debugger to map Typescript files to Javascript ones, we set the outFiles directories according to the npm build script

Note that we use the variable workspaceFolder which is part of the launch.json specification

Emacs equivalent of launch.json

You can also configure the debugger using Emacs lisp function dap-register-debug-template. Here is the equivalent of the launch.json above:

(dap-register-debug-template
  "Launch index::(Emacs DAP template)"
  (list :type "node"
        :request "launch"
        :program "${workspaceFolder}/index.ts"
        :dap-compilation "npm run build"
        :outFiles (list "${workspaceFolder}/dist/**/*.js")
        :name "Launch index (Emacs DAP template)"))

Typescript compiler configuration

In order to use the debugger with Typescript files mapping, you have to set a few options in tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "outDir": "dist",
    "sourceMap": true,
    "esModuleInterop": true
  }
}

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 theme

Also, don’t skip the tutoriel Elisp , and use C-h f/C-h v commands to describe the features available in Emacs

Fill this form to get the init.el used in this tutorial:

Séparez les adresses email par une virgule.

Merci pour votre feedback

Notre équipe vous renverra un message dès que possible.
En attendant, nous vous invitons à visiter notre site web.

Sign in to leave a comment
Installer Ansible avec Python3 (Ubuntu, Debian)