akermu / emacs-libvterm

Emacs libvterm integration
GNU General Public License v3.0
1.72k stars 137 forks source link

comint-input-ring usage #336

Closed azzamsa closed 4 years ago

azzamsa commented 4 years ago

Can we use comint in vterm buffer?

I tried helm-comint-input-ring in vterm buffer it doesn't work. But it works in other e/shell/ansi-term buffer.

Thank you for vterm.

brotzeit commented 4 years ago

vterm currently doesn't fill helm-comint-input-ring.

I'm using a custom helm source on a file and modified vterm-send-return so it appends the input to this file.

(setq term-prompt-regexp "^[^#$%>\\n]*[#$%>] *")
(defvar vterm-history "path/to/vterm_history")

(defun vterm-send-return ()
  "Send `C-m' to the libvterm."
  (interactive)
  (when vterm--term
    (let* ((beg (vterm--get-prompt-point))
          (end (vterm--get-end-of-line))
          (string (buffer-substring-no-properties beg end))
          (file vterm-history))
      (write-region (concat string "\n") nil file t 0))
    (if (vterm--get-icrnl vterm--term)
        (process-send-string vterm--process "\C-j")
      (process-send-string vterm--process "\C-m"))))

(defun helm-vterm-history ()
  (interactive)
  (let ((contents (with-temp-buffer
                    (insert-file-contents vterm-history)
                    (reverse (split-string (buffer-string) "\n")))))
    (helm :sources `((name . "vterm history")
                     (candidates . contents)
                     (action . (lambda (candidate)
                                 (vterm-send-string candidate))))
          :candidate-number-limit 10000)))

I noticed this still needs some improvements :P

azzamsa commented 4 years ago

Great!. It works like a charm.

Thank you for vterm.

azzamsa commented 4 years ago

@brotzeit I've added the check to ignore empty string so that it doesn't get written into the history file.

image

azzamsa commented 4 years ago

I've some problem with the code:

  1. duplicate entries
  2. appending empty string.

So here is my updated version:

I'll updated the code here if I find a bug

(defun get-current-cmd ()
    (let* ((beg (vterm--get-prompt-point))
           (end (vterm--get-end-of-line)))
      (buffer-substring-no-properties beg end)))

  (defun vterm-send-return ()
    "Send `C-m' to the libvterm."
    (interactive)
    (when vterm--term
      (let* ((current-cmd (get-current-cmd)))
        (if (not (string= current-cmd ""))
            (add-to-list 'vterm-history current-cmd)))

      ;; default part from vterm.el
      ;; (setq cursor-type 'bar)
      (if (vterm--get-icrnl vterm--term)
          (process-send-string vterm--process "\C-j")
        (process-send-string vterm--process "\C-m"))))

  (defun helm-vterm-history ()
    (interactive)
    (let ((histories vterm-history)
          (current-cmd (get-current-cmd)))
      (helm :sources `((name . "vterm history")
                       (candidates . histories)
                       (action . (lambda (candidate)
                                   (vterm-send-string candidate))))
            :input current-cmd
            :candidate-number-limit 10000))))
brotzeit commented 4 years ago

Instead of checking if the string is empty, I don't append strings that are too long. For example when you use return on the output of systemctl status, you get the whole output appended to your history every time you press enter.

Maybe somebody finds a better way to handle this. It's really convenient to have a global history.

azzamsa commented 4 years ago

Instead of checking if the string is empty, I don't append strings that are too long. For example when you use return on the output of systemctl status, you get the whole output appended to your history every time you press enter.

I can't reproduce this behavior with the code above

brotzeit commented 4 years ago

Oh then I have to try it :+1:

theottm commented 1 year ago

I also looked for something like that. I'm using the completing-read ecosystem though. I implemented something this morning.

I am mostly interested in the history of zsh, so I ended up reading the zsh history directly. Duplicates are already handled by zsh so this is nice. Here is the code maybe it helps someone in the future:

;; this function is from http://xahlee.info/emacs/emacs/elisp_read_file_content.html
(defun read-lines-from-files (filePath)
  "Return a list of lines of a file at filePath."
  (with-temp-buffer
    (insert-file-contents filePath)
    (split-string (buffer-string) "\n" t)))

(defun vterm-history (histfile)
  (vterm-send-string (completing-read "Commands: " (read-lines-from-files histfile)) t))

(defun vterm-history-zsh ()
  (interactive)
  (vterm-history "~/.config/zsh/.histfile"))

(define-key vterm-mode-map (kbd "C-c C-q") 'vterm-history-zsh)

As you see this can be easily adapted to read from any history file, even from a combination of history files if needed.

It probably also relates to https://github.com/minad/consult/issues/121