BIRKEY CONSULTING

ABOUT  GITHUB  RSS  ARCHIVE  DONATE


10 Jul 2021

Why Eshell? - Part 3

If you have been following my `Why Eshell` series, you might be wondering about interactive ido completion for eshell. Since Eshell buffer is just a regular emacs buffer, we can compose few emacs builtin functionalities to bend it to our command line work flow. Following is all the code you need. Note that it is heavily commented so you can understand what is happening.

(defun kshell-with-name (&optional name)
  ;; creates an eshell buffer with `kshell' as the default name. Take
  ;; optional name to override
  (let ((m (->> (or name "kshell")
		(format "*%s*"))))
    (if (get-buffer m)
	(pop-to-buffer m)
      (progn
	(eshell)
	(rename-buffer m)))))

(defun kshell (&optional cmd buff-name send-input)
  ;; interactive fn that you can call via M-x or hotkey. It detects
  ;; current project root to start eshell buffer if no `buff-name' is
  ;; given. You can control how the `cmd` gets insert only or insert
  ;; and executed using `send-input` flag. Ex. Useful for further
  ;; editing.
  (interactive)
  (let ((dir (projectile-project-root))) ;; you need projectile
    (if buff-name
	(kshell-with-name buff-name)
      (kshell-with-name))
    (eshell/clear-scrollback)
    (insert (format "cd %s" (or dir "~/")))
    (eshell-send-input)
    (insert (format "%s" cmd))
    (when send-input (eshell-send-input))))

(defun krun (cmd)
  ;; eshell command history with ido completion. Assign it to a hot
  ;; key say, F12 and you will get a search-able command history that
  ;; you can execute just by doing ido interactive search.
  (interactive
   (list
    (ido-completing-read
     "Enter cmd to run (append ##name for buffer name): "
     (let ((history nil))
       ;; We have to build up a list ourselves from the ring vector.
       (dotimes (index (ring-length keshell-history-global-ring))
	 (push (ring-ref keshell-history-global-ring index) history))
       ;; Show them most-recent-first.
       (setq history (nreverse history))))))
  (let* ((cmds (split-string cmd "##")) ;; you need s.el lib
	 (tag (or (-> cmds second)
		  "kshell"))
	 (buff-name (-> tag s-chomp s-trim)))
    (kshell cmd buff-name)))

Here is the screen recording in action:

eshell-ido-interactive.gif

I am using F12 to invoke `krun` and my cross session eshell history shows up in the minibuffer where I can use ido completion to select a command that I can run. As an added benefit, above code allows one to bring a eshell buffer with specific name by adding ##name at the end of the command if one wants to have the command run in a dedicated buffer.

Tags: eshell emacs