;;; acdw-eshell.el -*- lexical-binding: t; coding: utf-8-unix -*-

;; Author: Case Duckworth <(rot13-string "npqj@npqj.arg")>
;; Keywords: configuration
;; URL: https://tildegit.org/acdw/emacs

;; This file is NOT part of GNU Emacs.

;;; License:
;; Everyone is permitted to do whatever with this software, without
;; limitation.  This software comes without any warranty whatsoever,
;; but with two pieces of advice:
;; - Don't hurt yourself.
;; - Make good choices.

;;; Commentary:

;;; Code:

(require 'cl-lib)


;;; Eshell starting and quitting

(defun eshell-quit-or-delete-char (arg)
  "Delete the character to the right, or quit eshell on an empty line."
  (interactive "p")
  (if (and (eolp) (looking-back eshell-prompt-regexp))
      (eshell-life-is-too-much)
    (delete-forward-char arg)))

;;;###autoload
(defun eshell-pop-or-quit (&optional buffer-name)
  "Pop open an eshell buffer, or if in an eshell buffer, bury it."
  (interactive)
  (if (eq (current-buffer) (get-buffer (or buffer-name "*eshell*")))
      (eshell-life-is-too-much)
    (with-message "Starting eshell"
      (eshell))))


;;; Insert previous arguments
;; Record arguments

(defvar eshell-arg-history nil)
(defvar eshell-arg-history-index nil)
(add-to-list 'savehist-additional-variables 'eshell-arg-history)

(defun eshell-record-args (&rest _)
  "Record unique arguments onto the front of `eshell-arg-history'."
  (setq eshell-arg-history
        (cl-loop with history = eshell-arg-history
                 for arg in (reverse eshell-last-arguments)
                 do (setq history (cons arg (remove arg history)))
                 finally return history)))

(defun eshell-insert-prev-arg ()
  "Insert an argument from `eshell-arg-history' at point."
  (interactive)
  (if (eq last-command 'eshell-insert-prev-arg)
      (progn
        (let ((pos (point)))
          (eshell-backward-argument 1)
          (delete-region (point) pos))
        (if-let ((text (nth eshell-arg-history-index
                            eshell-arg-history)))
            (progn
              (insert text)
              (cl-incf eshell-arg-history-index))
          (insert (cl-first eshell-arg-history))
          (setq eshell-arg-history-index 1)))
    (insert (cl-first eshell-arg-history))
    (setq eshell-arg-history-index 1)))

(add-hook 'eshell-mode-hook
          (lambda ()
            (add-hook 'eshell-post-command-hook
                      #'eshell-record-args nil t)
            (local-set-key (kbd "M-.") #'eshell-insert-prev-arg)))

;;;###autoload
(define-minor-mode eshell-arg-hist-mode
  "Minor mode to enable argument history, like bash/zsh with M-."
  :lighter "$."
  :keymap (let ((map (make-sparse-keymap)))
            (define-key map (kbd "M-.") #'eshell-insert-prev-arg)
            map)
  (if eshell-arg-hist-mode
      (add-hook 'eshell-post-command-hook #'eshell-record-args nil t)
    (remove-hook 'eshell-post-command-hook #'eshell-record-args t)))

(provide 'acdw-eshell)
;;; acdw-eshell.el ends here