1

I am pretty new to emacs (doom-emacs in this case) and I have an idea of what I want to do, but not sure how to do it.

What I am after is that when I open a .js file, I want to split my window into 3. one small window on the left where I want to run jest, and one small window on the right if there is a test file for the file opened in the buffer (following the format *.spec.(js|ts)).

is this doable in emacs or should I just give up on these dreams?

Drew
  • 75,699
  • 9
  • 109
  • 225
munHunger
  • 125
  • 3
  • I have a feeling this question might be a duplicate. Search the tags for this (put a tag in brackets ([]) to search for it). If you find a dup, please delete this one. Thx. – Drew Oct 21 '22 at 21:46

2 Answers2

1

It's definitely possible. Here is my initial attempt at creating the 3 window split with a jest test runner, a source file, and it's associated spec file. You can put this code in your config and try it out via M-x jest-open.

(defun jest-spec-for-file (file)
  "Given a file, return its matching spec file."
  (let* ((dirname  (file-name-directory    file))
         (filename (file-name-nondirectory file))
         (ext      (file-name-extension    file))
         (basename
          (replace-regexp-in-string (concat "." ext "$") "" filename)))
    (concat dirname basename ".spec." ext)))

(defvar jest-command "jest --watch"
  "This is the shell command used to run jest.")

(defun jest-run (&optional term)
  "Switch to the jest buffer if it exists or create a new one."
  (interactive)
  (if (get-buffer "jest") (switch-to-buffer "jest")
    (cond ((equal term 'vterm)
           (progn
             (vterm "jest")
             (vterm-send-string (concat jest-command "\n"))))
           (t
            (progn
             (term "/bin/bash")
             (rename-buffer "jest")
             (term-send-raw-string (concat jest-command "\n"))
             ))
           )))

(defun jest-open (file)
  "Create a 3 window layout containing jest|source|spec."
  (interactive "f")
  (let* ((spec (jest-spec-for-file file)))
    (delete-other-windows)
    (split-window-right)
    (split-window-right)
    (jest-run)             ; change this to (jest-run 'vterm)
    (windmove-right)
    (find-file file)
    (windmove-right)
    (find-file spec)
    (windmove-left)))

How It Works

We have 3 functions and a variable.

(jest-spec-for-file file)

Return the spec file associated with file. This is just pure string manipulation that takes a string like /tmp/index.js and returns /tmp/index.spec.js.

jest-command

This variable contains the shell command used to invoke jest. Feel free to adapt it to your needs.

(jest-run &optional term)

If a buffer named "jest" exists, switch to it. Otherwise, create a term (or vterm) buffer and run jest-command inside of it. I have it default to use term since it's included with stock emacs, but I highly recommend giving vterm a try. Anything that's even slightly animated will look better in vterm. When you're ready to make the switch, change the line that says:

(jest-run)

to

(jest-run 'vterm)

Also, keep in mind that if you hit q to stop jest, this function won't try to restart it. Once the jest buffer is created, all control is deferred to you.

(jest-open file)

Finally, we have the function that creates the 3 window split and sets up the buffers such that you have a jest test runner on the left, the source in the middle, and a spec file on the right. It will also place the focus on the middle window, because I think that's what would be most intuitive.

You can give it a try by hitting M-x jest-open and selecting a file when prompted. If you end up wanting to use this frequently, you can bind it to a key. You can do it the doom way too.

PS

I tried to write this in a way that works in any Emacs configuration. It should work just fine in Doom. As you explore this code (or any other elisp), I encourage you to use C-h f look up the docs for functions. I also like using M-x ielm to start up an interactive elisp repl to feel how things work. Doing this really accelerated my elisp self-education.

g-gundam
  • 1,096
  • 1
  • 3
  • 13
0

Use M-x window-configuration-to-register (bound to C-x r w in vanilla Emacs, not sure about Doom). From the documentation:

window-configuration-to-register is an interactive and natively compiled function defined in register.el.gz.

Signature (window-configuration-to-register REGISTER)

Documentation Store the window configuration of the selected frame in register REGISTER.

Use C-x r j to restore the configuration. Argument is a character, naming the register.

Interactively, reads the register using register-read-with-preview.

Phil Hudson
  • 1,651
  • 10
  • 13
  • The problem I had with `window-configuration-to-register` is that it's really hard to persist the value of a `#` that's in a register between restarts of emacs. However, I did come up with an [alternative](https://emacs.stackexchange.com/questions/73842/how-do-you-serialize-a-window-configuration/73858#73858). – g-gundam Oct 26 '22 at 20:58
  • 1
    You need desktop mode (desktop.el) -- that lets you persist anything you specify. – Phil Hudson Oct 26 '22 at 22:48
  • I'll look into that. Thanks. – g-gundam Oct 26 '22 at 23:20