5

I've got helm installed to work with comint mode either in eshell or when running python in an inferior comint process. I'd like to preserve the command line history, especially in the python mode.

Tried

(setq savehist-additional-variables '(comint-input-ring))

but that seems to result in the savehist file always containing

(setq comint-input-ring 'nil)

Other fields like search-ring, kill-ring, minibuffer-history save fine. Is there a way to do this?

I know this link answers it for a 'real' shell but it doesn't work in this case.

EDIT As requested, what I've tried is the following, and it describes my use-case

(helm-mode 1)
(define-key comint-mode-map (kbd "C-S-l" 'helm-comint-input-ring)

elsewhere in .emacs

(require 'python-mode) ;; Goes to ipython.exe (on windows)

Coding some python, using C-c C-c to send to ipython process.

>>> somefunc()

This now appears if I use C-S-l as a helm-comint-input-ring and I can search on it.

I'd like to preserve these commands as I tend to repeat them across sessions (but with enough variability to not script it away completely).

I agree there could be both eshell, python , other comint buffers running, I suppose you are saying that ipython should preserve the history buffer and I should somehow get savehist to load that? Not sure how.

1
  • Now that I've looked at your link, this question seems like a duplicate of that. Constantine's answer is applicable here, with appropriate modifications (and the caveat that it assumes a single comint buffer for a given mode). You say it "doesn't work"; please show us what you tried.
    – phils
    Mar 2, 2015 at 0:11

3 Answers 3

4

I followed this guide: https://oleksandrmanzyuk.wordpress.com/2011/10/23/a-persistent-command-history-in-emacs/ and also took hint of a comment in the similar SO-question you mentioned (which advised using the LOCAL argument of add-hook, but see note below!). So this seems to work for me:

(defun comint-write-history-on-exit (process event)
  "Write comint history of PROCESS when EVENT happened to a file specified in buffer local var 'comint-input-ring-file-name' (defined in turn-on-comint-history)."
  (comint-write-input-ring)
  (let ((buf (process-buffer process)))
    (when (buffer-live-p buf)
      (with-current-buffer buf
        (insert (format "\nProcess %s %s" process event))))))

(defun turn-on-comint-history ()
  "Setup comint history.

When comint process started set buffer local var
'comint-input-ring-file-name', so that a file name is specified to write
and read from comint history.

That 'comint-input-ring-file-name' is buffer local is determined by the
4th argument to 'add-hook' below.  And localness is important, because
otherwise 'comint-write-input-ring' will find mentioned var nil."

  (let ((process (get-buffer-process (current-buffer))))
    (when process
      (setq comint-input-ring-file-name
            (format "~/.emacs.d/inferior-%s-history"
                    (process-name process)))
      (comint-read-input-ring)
      (set-process-sentinel process
                            #'comint-write-history-on-exit))))

(add-hook 'inf-gf-mode-hook 'turn-on-comint-history nil t)

The history will be saved calling 'comint-send-eof' (usually C-c C-d). (Using 'kill-this-buffer' will not save the history.)

NOTE regarding localness: I'm not really sure what happened today, but I had to run add-hook without LOCAL argument to get it working:

(add-hook 'inf-gf-mode-hook 'turn-on-comint-history nil nil)
1
  • i wish the history files were encrypted.
    – Ben Hyde
    Jun 28, 2017 at 15:48
1

AFAIK the global binding for comint-input-ring will almost certainly be nil, as it is given a buffer-local value in comint buffers (for the obvious reasons of not blending all the histories of all your comint buffers together).

As such, it won't work with savehist in that way (and perhaps nor would you want it to).

You should work out exactly how you want this to work, bearing in mind that there may be multiple coincidental comint buffers (including multiple buffers using the exact same mode), so that you can work out what you should restore from any given history in a subsequent Emacs instance.

0

Seconding @rogoshijn's answer: use the instructions from https://oleksandrmanzyuk.wordpress.com/2011/10/23/a-persistent-command-history-in-emacs/

Please bear in mind, that you might run into conflicts when running multiple python processes at the same time: when they get killed, they are going to compete with each other on who gets to write the history, so overrides will happen unless you further refine the comint-input-ring-file-name for those additional processes (aka make them unique so that they don't get shared between processes).

I am adding more comments to the original solution so that future Emacs newbies will find it easier to understand and further customize to their needs, liking.

;; saves comint history if there is any (some buffers don't have
;; comint history, and that is fine, that will be a NOOP
;; note: this will write history the to buffer-level variable: comint-input-ring-file-name
(defun comint-write-history-on-exit (process event)
  (comint-write-input-ring)
  (let ((buf (process-buffer process)))
    (when (buffer-live-p buf)
      (with-current-buffer buf
        (insert (format "\nProcess %s %s" process event))))))


;; sets the comint-input-ring-file-name variable for your process buffer
;; loads all the historical data from that file
;; ensures that when your comint process ends, we save the history back in that file
(defun turn-on-comint-history ()
  (let ((process (get-buffer-process (current-buffer))))
    (when process
      (setq comint-input-ring-file-name
            (format "~/.emacs.d/inferior-%s-history"
                    (process-name process)))
      (comint-read-input-ring)
      (set-process-sentinel process
                            #'comint-write-history-on-exit))))

;; Whenever python-mode is enabled, activate the comint history
(add-hook 'python-mode-hook 'turn-on-comint-history)



;;
;; next section is for safeguards: you want the history to be saved even if comint buffers get killed or even if emacs reboots.
;;



;; try to write comint history on buffer kill (only impacts if that buffer is a comint buffer)
(add-hook 'kill-buffer-hook 'comint-write-input-ring)

;; try to write commint history also when emacs gets killed
(defun mapc-buffers (fn)
  (mapc (lambda (buffer)
          (with-current-buffer buffer
            (funcall fn)))
        (buffer-list)))

(defun comint-write-input-ring-all-buffers ()
  (mapc-buffers 'comint-write-input-ring))

(add-hook 'kill-emacs-hook 'comint-write-input-ring-all-buffers)
3
  • I'm not sure why you felt compelled to add an answer to this question, given that a very similar answer was given eight years ago: it may be that you are adding valuable information, but without any explanation, how is anybody going to figure out whether it is useful or not? And your comment seems like an important warning, so why add it as a comment? You should make your answer as self-contained as possible, by incorporating the warning into it (and deleting the comment). But you really should edit your answer and explain what it offers over and above what the previous answer offered.
    – NickD
    Mar 15, 2023 at 21:28
  • Updating the original answer now! Thank you for the feedback! Where I was coming from is that I did a similar setup recently and I wanted to add more details about how this all fits together by using comments in the hopes that future Emacs newbies (like me) would find it useful. Mar 16, 2023 at 23:19
  • Another reason behind my comment was to have a complete answer so that in case this website link (oleksandrmanzyuk.wordpress.com/2011/10/23/…) is not available anymore, we still have a complete solution. Mar 16, 2023 at 23:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.