'Programming' Articles

Node.js stack traces in Emacs compilation-mode

, ,

I’ve been using Emacs regularly for at least 10 years, for all of my programming tasks that don’t involve Java or .NET. Amazingly, for most of that time I’ve used a pretty stock installation of Emacs with only a few cargo-culted customizations stolen from my co-workers’ .emacs files. However, as part of switching to a new job in the last month, I’ve taken the opportunity to invest in my shell and editor, and I’ve fallen deep down the rabbit hole of Emacs customizations. My continually-evolving config is publically available on GitHub. The difference between stock Emacs and one loaded up with custom packages is astounding - I can’t believe that I had been missing out on all of this. But this is not an article about setting up Emacs to be your perfect editor. Instead, I wanted to provide a tip for Node developers that I hadn’t found the answer to when I went looking.

The other day I was working on a Node.js script, and I was constantly switching back and forth from Emacs to the terminal to run the script, hit some error, remember the line number, go back to Emacs to look up the error, etc. Then I remembered that Emacs has the compile command, which will run a shell command and show the output in a separate window. Now I could split my frame into two side-by-side windows, edit my script on the left, and run compile to test out my script with any test command I liked (and then use recompile or g in the *compilation* buffer to re-run the command). But there’s another neat feature of compilation-mode - it can understand stack traces and provide hotkey access to the line that triggered an error. Unfortunately, the stack traces from Node were not being recognized correctly, and Emacs thought the last number was the line number, when it’s really the column number.

To fix this, I simply needed to add a new regexp to the list of patterns compilation-mode understands:

;; Add NodeJS error format
(setq compilation-error-regexp-alist-alist
      (cons '(node "^[  ]+at \\(?:[^\(\n]+ \(\\)?\\([a-zA-Z\.0-9_/-]+\\):\\([0-9]+\\):\\([0-9]+\\)\)?$"
                         1 ;; file
                         2 ;; line
                         3 ;; column
                         )
            compilation-error-regexp-alist-alist))
(setq compilation-error-regexp-alist
      (cons 'node compilation-error-regexp-alist))

Writing that regexp would have been hard, but of course Emacs has a solution. First I found the current value of compilation-error-regexp-alist-alist using describe-variable in order to get an example regexp to start with. Then, in the compilation buffer where my stack trace was, I ran re-builder, which pops up a little mini window where I could tweak my regexp and see immediately whether or not it was matching correctly. Then I just added this to my init.el, evaluated it with eval-region, and my next compile had the errors highlighted correctly, and I could immediately go to the offending code with next-error or by putting the point on the stack trace line in the compilation buffer and pressing RET.

Hopefully this helps out some fellow Emacs-using Node developers, and serves as a reminder that your tools can always work better for you.