Chad Stovern

JavaScript in Emacs Revisted

My blog is in danger of becoming only about Emacs with a dash of musings on JavaScript, but I think it's important to document my findings after working solely in Emacs as a Software Engineer for the past five months.

This is by no means a complete guide to getting set up to work as a JavaScript developer in Emacs, but should give you the tools you need to find your own path with some ease.

What's in a Mode?

First came my realization that I could use rjsx-mode for all JavaScript files. Since this mode is just an extension of the popular js2-mode, I dropped the logic from my configuration to try and figure out if a file contained React JSX or not. I have not had any issues using rjsx-mode for editing all JavaScript files.

(use-package rjsx-mode
  :mode ("\\.js\\'"
         "\\.jsx\\'")
  :config
  (setq js2-mode-show-parse-errors nil
        js2-mode-show-strict-warnings nil
        js2-basic-offset 2
        js-indent-level 2)
  (setq-local flycheck-disabled-checkers (cl-union flycheck-disabled-checkers
                                                   '(javascript-jshint))) ; jshint doesn't work for JSX
  (electric-pair-mode 1)
  (evil-leader/set-key-for-mode 'rjsx-mode
    "fu"  #'lsp-find-references          ; (f)ind (u)sages
    "fp" 'prettier-js-mode))             ; (f)ormat (p)rettier

Think Globally, Run Locally

The add-node-modules-path package is great for automatically detecting and using npm package binaries which are locally installed in the project you're actively editing, such as eslint, prettier, etc. This allowed me to no longer maintain similar functionality I had written myself as Emacs Lisp functions.

(use-package add-node-modules-path
  :defer t
  :hook (((js2-mode rjsx-mode) . add-node-modules-path)))

I Feel Pretty

On my current team we have settled on using ESLint with Prettier. Emacs has a great package that will automatically apply Prettier rules to the current buffer on save. By default it will use the Prettier config in the current project. I highly recommend this over setting global defaults in your Emacs config. This allows you to just write code and not worry about formatting. I added a shortcut of ,fp to toggle this behavior on and off in the event I don't want to apply Prettier rules.

(use-package prettier-js
  :defer t
  :diminish prettier-js-mode
  :hook (((js2-mode rjsx-mode) . prettier-js-mode))
  :init
  (evil-leader/set-key-for-mode 'rjsx-mode
    "fp" 'prettier-js-mode)) ; (f)ormat (p)rettier

You Complete Me

Most importantly, like my colleagues using Visual Studio Code (VSCode), I wanted full-featured code completion, function definition and usage lookups. lsp-mode works great for this. Just like VSCode, It uses Microsoft's TypeScript Language Server behind the scenes. In addition to the emacs setup you need to globally install those supporting tools.

npm i -g typescript-language-server; npm i -g typescript

I added some shortcuts for jumping to a definition, and then jumping back. In the JavaScript configuration above their is also a shortcut for "find usages" which is very handy when working in a code base of any size.

I highly recommend reading the configuration below, the embedded comments, and the lsp-mode documentation to get a feel for the options you may want. The main thing for me was not allowing lsp-mode to suppress and replace the detected default linter for a project.

(use-package lsp-mode
  :defer t
  :diminish lsp-mode
  :hook (((js2-mode rjsx-mode) . lsp))
  :commands lsp
  :config
  (setq lsp-auto-configure t
        lsp-auto-guess-root t
        ;; don't set flymake or lsp-ui so the default linter doesn't get trampled
        lsp-diagnostic-package :none)
  ;;; keybinds after load
  (evil-leader/set-key
    "jd"  #'lsp-goto-type-definition ; (j)ump to (d)efinition
    "jb"  #'xref-pop-marker-stack))  ; (j)ump (b)ack to marker

(use-package company-lsp
  :defer t
  :config
  (setq company-lsp-cache-candidates 'auto
        company-lsp-async t
        company-lsp-enable-snippet nil
        company-lsp-enable-recompletion t))

(use-package lsp-ui
  :defer t
  :config
  (setq lsp-ui-sideline-enable t
        ;; disable flycheck setup so default linter isn't trampled
        lsp-ui-flycheck-enable nil
        lsp-ui-sideline-show-symbol nil
        lsp-ui-sideline-show-hover nil
        lsp-ui-sideline-show-code-actions nil
        lsp-ui-peek-enable nil
        lsp-ui-imenu-enable nil
        lsp-ui-doc-enable nil))

Parting Thoughts

I'm pretty happy with my setup so far, and haven't run into any issues or missing features compared to running VSCode. I'm also using TypeScript, which may spawn a follow-up post. Here is a link to the JavaScript / TypeScript section of my living Emacs Configuration.

Clojure is my favorite programming language, but JavaScript has become my second:

  • I can write mostly functional code.
  • I can avoid the complexity of classes using modules which are essentially namespaces.
  • I can get opt-in types and interfaces, similar to clojure.spec, via TypeScript.
  • I can get a REPL-lite experience via Jest in –watch mode; yet nothing beats a REPL.

If you made it this far, you may be as crazy as I am. Happy Hacking!


Javascript Support in Emacs

Two hills I'm prepared to die on are the superiority of modal editing and the near limitless power and customization of Emacs.

While vscode, atom, etc are nice and have decent communities building plugins around them, I'll be configuring emacs to take me on my journey to learn javascript.

There is a built in javascript mode with very basic functionality but we want better highlighting and parsing plus syntax checking. This is where js2-mode comes in: http://elpa.gnu.org/packages/js2-mode.html

I'm going to make two assumptions here:

  1. You already have flycheck installed or will go read about it and set it up yourself.
  2. You are using use-package to manage your package installs and configuration like me, or know how to adapt my examples for your own preferred method.

This snippet will get js2-mode installed and set as the default mode for all .js files you edit. It is also setting some indentation preferences and disabling some built in syntax checking options (we'll be leveraging flycheck and jshint).

;; a better javascript mode
(use-package js2-mode
  :mode "\\.js\\'"
  :config
  (setq js2-mode-show-parse-errors nil
        js2-mode-show-strict-warnings nil
        js2-basic-offset 2
        js-indent-level 2)
  (electric-pair-mode 1))

Now we can install jshint for linting / syntax checking purposes.

npm install -g jshint

Followed by creating a global .jshintrc file in our home directory. See a full set of options here: .jshintrc

{
  "asi"       : true,
  "esversion" : 6
}

We're now ready to start hacking around with javascript code with full syntax checking and highligting.

Better code auto-completion would be nice, but we'll tackle that next time. B-)


Using Paredit With Javascript Modes

I'm a huge fan of Emacs and a huge fan of programming using Clojure. I've been trying to avoid Javascript, but it's reach is everywhere and learning it will definitely help my day job efforts helping teams create cloud native applications.

One hurdle I faced in setting up Emacs for Javascript development was paredit inserting a space before opening parens in Javascript and JSON modes; leaving me with things like console.log ("foo") rather than console.log("foo") as I merily typed along.

Even though non-lisps don't lend themselves to structural editing the way lisps do, I still like being able to wrap and manipulate parens, braces, quotations, etc with paredit—so I did not want to disable paredit in these modes.

Here is the quick fix I am using from my emacs configuration:

;; prevent paredit from adding a space before opening paren in certain modes
(defun cs/mode-space-delimiter-p (endp delimiter)
  "Don't insert a space before delimiters in certain modes"
  (or
   (bound-and-true-p js2-mode)
   (bound-and-true-p js-mode)
   (bound-and-true-p javascript-mode)))
(add-to-list 'paredit-space-for-delimiter-predicates #'cs/mode-space-delimiter-p))

Happy hacking; and until next time…

“If you think paredit is not for you then you need to become the kind of person that paredit is for.”


Emacs is a Way of Life

Emacs is an operating system.

Emacs has a never-ending learning curve.

Emacs is a way of life.

All of these things are both a joke and largely true. As a die hard Vim fan, I never thought I'd fall into such enthusiastic use of Emacs, yet I have and don't want to look back.

I started down this path because of my interest in learning Common Lisp and then Clojure. The Emacs tooling for lisps is superb and once I learned you could configure text editing in Emacs to behave identically to Vim, I set out on my spirit quest.

With multiple years under my belt using, configuring, and solving my problems with Emacs, I'd like to start sharing some of these tips, tricks, and use cases. In the meantime I invite anyone to skim through my living literate config below.

Chad's Literate Emacs Configuration

Happy hacking!

Learning Curve for Emacs