My solution is to wrap the function eshell-write-history
. It is called when you exit intentionally, but also in other places, e.g. when you close Emacs without quitting eshell first. You want that history as well, and if you only wrap eshell-exit-hook
you risk losing it: yes that hook is called on Emacs exit, but because it, itself, calls eshell-write-history
, it risks undoing your custom history work.
Long story short: patch eshell-write-history
to just force always appending history.
There's another gotcha which is explained in the comments.
Code:
;; Record the size of existing history when eshell starts
(add-hook 'eshell-mode-hook
(lambda ()
(setq-local hly/eshell-history-on-start (ring-length eshell-history-ring))))
(defun hly/eshell-write-history-around (orig-fun &rest args)
"Always force ‘eshell-write-history’ in append mode"
;; I can’t use ring-resize because eshell uses ring-insert-at-beginning on the
;; history ring, and that doesn’t play nicely with ring-resize when the
;; original ring was larger than the total history. Iterating and copying
;; one-by-one is the only option I can think of.
(let* ((full eshell-history-ring)
(n (- (ring-length eshell-history-ring) hly/eshell-history-on-start))
(eshell-history-ring (make-ring n)))
(cl-loop for i downfrom (1- n) to 0
do (ring-insert eshell-history-ring (ring-ref full i)))
(cl-destructuring-bind (&optional fname append) args
(funcall orig-fun fname t))))
Bonus customization which I recommend: change the default history file, lest you accidentally overwrite it one day running eshell from within emacs -Q
:
(setq eshell-history-file-name "~/.emacs.d/eshell/history-my")
P.S.: Beware eshell-save-history-on-exit
: that only controls the behavior on Emacs exit I described above, not regular eshell exit. From the docs (emphasis mine):
**History is always preserved after sanely exiting an Eshell buffer.**
However, when Emacs is being shut down, this variable determines
whether to prompt the user.
If set to nil, it means never save history on termination of Emacs.
If set to ask, ask if any Eshell buffers are open at exit time.
If set to t, history will always be saved, silently.