2017-06-12 22:26:50 +02:00
|
|
|
|
;;; Eshell
|
2016-10-14 14:03:30 +02:00
|
|
|
|
|
2017-06-16 20:53:25 +02:00
|
|
|
|
;;; Eshell gets initialized differently. When eshell.el first gets loaded, only
|
|
|
|
|
;;; the core is defined and `eshell-load-hook' is called. For every Eshell
|
|
|
|
|
;;; session, `eshell-mode' is run: it resets `eshell-mode-map', it loads
|
|
|
|
|
;;; modules, runs their hooks and concludes with `eshell-first-time-mode-hook'
|
|
|
|
|
;;; (for the first session only) and `eshell-mode-hook'.
|
|
|
|
|
|
2017-10-25 20:12:52 +02:00
|
|
|
|
;; Emacs pinentry for GPG.
|
|
|
|
|
(require 'main)
|
|
|
|
|
|
2017-11-05 12:39:37 +01:00
|
|
|
|
;;; REVIEW: If history contains read-only text (e.g. accidental insertion of the prompt),
|
|
|
|
|
;;; `eshell-write-history' won't work.
|
|
|
|
|
;;; See #29153.
|
2017-11-03 14:55:57 +01:00
|
|
|
|
|
2017-10-23 21:06:08 +02:00
|
|
|
|
;;; REVIEW: ANSI coloring goes wrong sometimes. Quite often with emerge/eix.
|
|
|
|
|
;;; Fixed in #27407.
|
|
|
|
|
(require 'patch-eshell)
|
|
|
|
|
|
|
|
|
|
;;; TODO: Sometimes transmission-daemon does not start from Eshell.
|
|
|
|
|
|
2017-11-05 12:39:37 +01:00
|
|
|
|
;;; REVIEW: Redirecting big output to file (e.g. /dev/null) is extremely slow.
|
2017-10-10 16:19:22 +02:00
|
|
|
|
;; > cat /usr/share/dict/british-english | wc -l
|
|
|
|
|
;;; The above line yields rancom results. Plus it's much slower than
|
|
|
|
|
;; > wc -l /usr/share/dict/british-english
|
|
|
|
|
;;; while it should only cost an additional process to launch.
|
2017-11-05 12:39:37 +01:00
|
|
|
|
;;; See #29154.
|
2017-08-01 20:55:50 +02:00
|
|
|
|
|
2017-09-02 12:14:09 +02:00
|
|
|
|
;;; REVIEW: Cannot "C-c C-c" during a `sudo pacman -Syu`. A bug was filed about that already.
|
2017-07-29 11:14:18 +02:00
|
|
|
|
|
2017-08-01 20:55:50 +02:00
|
|
|
|
;;; TODO: The buffer stutters when writing "in-place", e.g. pacman, git.
|
|
|
|
|
;;; It seems that it does not do it as much in `emacs -Q`.
|
|
|
|
|
|
|
|
|
|
;;; REVIEW: `eshell/sudo' should not read -u in command arguments.
|
|
|
|
|
;;; This fails: sudo pacman -Syu --noconfirm.
|
|
|
|
|
;;; http://www.gnu.org/software/emacs/manual/html_node/eshell/Built_002dins.html
|
|
|
|
|
;;; https://emacs.stackexchange.com/questions/5608/how-to-let-eshell-remember-sudo-password-for-two-minutes
|
|
|
|
|
;;; See http://debbugs.gnu.org/cgi/bugreport.cgi?bug=27411
|
2017-09-02 12:14:09 +02:00
|
|
|
|
;;; and #28323.
|
|
|
|
|
|
2017-07-28 09:12:25 +02:00
|
|
|
|
;;; REVIEW: Eshell/Shell completion fails when PATH has a non-readable element.
|
2017-09-02 12:14:09 +02:00
|
|
|
|
;;; See https://github.com/emacs-helm/helm/issues/1785
|
|
|
|
|
;;; and https://debbugs.gnu.org/cgi/bugreport.cgi?bug=27300.
|
|
|
|
|
|
|
|
|
|
;;; REVIEW: 40M+ output: Stack overflow in regexp matcher
|
|
|
|
|
;;; See bug#28329.
|
2017-11-06 22:07:48 +01:00
|
|
|
|
;;; I guess the chunking is significant too. Could you try saving
|
|
|
|
|
;;; the output chunks, with this:
|
|
|
|
|
;;
|
|
|
|
|
;; (defvar eshell-chunk-number 0)
|
|
|
|
|
;; (defconst eshell-output-chunk-dir "eshell-output")
|
|
|
|
|
;; (make-directory eshell-output-chunk-dir t)
|
|
|
|
|
;;
|
|
|
|
|
;; (defun catch-eshell-output-chunk ()
|
|
|
|
|
;; (write-region eshell-last-output-block-begin
|
|
|
|
|
;; eshell-last-output-end
|
|
|
|
|
;; (format "%s/chunk.%d"
|
|
|
|
|
;; eshell-output-chunk-dir
|
|
|
|
|
;; eshell-chunk-number)
|
|
|
|
|
;; nil :quiet)
|
|
|
|
|
;; (setq eshell-chunk-number (1+ eshell-chunk-number)))
|
|
|
|
|
;;
|
|
|
|
|
;; (add-hook 'eshell-output-filter-functions
|
|
|
|
|
;; 'catch-eshell-output-chunk)
|
|
|
|
|
;;;
|
|
|
|
|
;;; And then afterwards 'cat eshell-output/chunk.*' should hopefully
|
|
|
|
|
;;; reproduce it?
|
|
|
|
|
|
2017-09-02 12:14:09 +02:00
|
|
|
|
|
|
|
|
|
;;; REVIEW: Eshell mixes stderr and stdout it seems.
|
|
|
|
|
;;; Example:
|
|
|
|
|
;;; $ mu find --nocolor --sortfield=d --maxnum=500 flag:unread AND NOT flag:trashed >/dev/null
|
|
|
|
|
;;; mu: no matches for search expression (4)
|
|
|
|
|
;;; See #21605 "24.3; Eshell not using stderr".
|
2017-07-28 09:12:25 +02:00
|
|
|
|
|
2017-11-05 12:39:37 +01:00
|
|
|
|
;;; REVIEW: Some parsing fails
|
2017-10-15 14:55:49 +02:00
|
|
|
|
;;; > echo -n $PATH | sed 's/:[^:]*sophos[^:]*/:/g'
|
|
|
|
|
;;; :s/:]*sophos[/:]*/:/"/
|
|
|
|
|
;;; Unknown modifier character ‘/’
|
|
|
|
|
;;;
|
|
|
|
|
;;; > date +%Z
|
2017-11-05 12:39:37 +01:00
|
|
|
|
;;; See #29157.
|
2017-10-15 14:55:49 +02:00
|
|
|
|
|
2017-10-24 18:30:34 +02:00
|
|
|
|
;;; TODO: Hour is printed twice. We don't need to set this?
|
|
|
|
|
;; (setq eshell-ls-date-format (replace-regexp-in-string "^\\+*" "" (getenv "TIME_STYLE")))
|
|
|
|
|
|
|
|
|
|
;;; TODO: ls: Sort using locale.
|
|
|
|
|
|
2017-11-05 12:39:37 +01:00
|
|
|
|
;;; REVIEW: `kill -#' does not work.
|
|
|
|
|
;;; See #29156.
|
2017-10-24 18:30:34 +02:00
|
|
|
|
|
2017-06-16 20:53:25 +02:00
|
|
|
|
(setq eshell-directory-name (concat emacs-cache-folder "eshell"))
|
|
|
|
|
|
2017-06-17 18:37:04 +02:00
|
|
|
|
;;; Use native 'sudo', system sudo asks for password every time.
|
|
|
|
|
(require 'em-tramp)
|
|
|
|
|
|
2017-11-05 12:39:37 +01:00
|
|
|
|
(with-eval-after-load 'esh-module
|
2017-06-17 18:37:04 +02:00
|
|
|
|
;; Don't print the banner.
|
|
|
|
|
(delq 'eshell-banner eshell-modules-list)
|
2017-06-17 19:01:32 +02:00
|
|
|
|
(push 'eshell-tramp eshell-modules-list))
|
2017-06-16 20:53:25 +02:00
|
|
|
|
|
2017-06-12 22:26:50 +02:00
|
|
|
|
(setq
|
|
|
|
|
eshell-ls-use-colors t
|
2017-06-18 22:25:17 +02:00
|
|
|
|
;; ffap-shell-prompt-regexp changes the behaviour of `helm-find-files' when
|
|
|
|
|
;; point is on prompt. I find this disturbing.
|
|
|
|
|
ffap-shell-prompt-regexp nil
|
2017-06-27 22:13:51 +02:00
|
|
|
|
eshell-history-size 1024
|
|
|
|
|
eshell-hist-ignoredups t
|
2017-06-15 18:56:22 +02:00
|
|
|
|
eshell-destroy-buffer-when-process-dies t)
|
|
|
|
|
|
2017-06-12 22:26:50 +02:00
|
|
|
|
;;; Leave `eshell-highlight-prompt' to t as it sets the read-only property.
|
2016-06-10 23:16:03 +02:00
|
|
|
|
(setq eshell-prompt-function
|
|
|
|
|
(lambda nil
|
|
|
|
|
(let ((path (abbreviate-file-name (eshell/pwd))))
|
2017-06-12 22:26:50 +02:00
|
|
|
|
(concat
|
2017-11-01 17:59:53 +01:00
|
|
|
|
(when eshell-status-p
|
2017-11-02 08:02:24 +01:00
|
|
|
|
(propertize (or (eshell-status-display) "") 'face font-lock-comment-face))
|
2017-06-12 22:26:50 +02:00
|
|
|
|
(format
|
2017-11-01 17:03:47 +01:00
|
|
|
|
(propertize "(%s@%s)" 'face '(:weight bold))
|
2017-06-12 22:26:50 +02:00
|
|
|
|
(propertize (user-login-name) 'face '(:foreground "cyan"))
|
2017-11-01 17:03:47 +01:00
|
|
|
|
(propertize (system-name) 'face '(:foreground "cyan")))
|
2017-11-01 18:22:05 +01:00
|
|
|
|
(if (and (require 'magit nil t) (or (magit-get-current-branch) (magit-get-current-tag)))
|
2017-11-01 17:03:47 +01:00
|
|
|
|
(let* ((root (abbreviate-file-name (magit-rev-parse "--show-toplevel")))
|
|
|
|
|
(after-root (substring-no-properties path (min (length path) (1+ (length root))))))
|
|
|
|
|
(format
|
|
|
|
|
(propertize "[%s/%s@%s]" 'face '(:weight bold))
|
|
|
|
|
(propertize root 'face `(:foreground ,(if (= (user-uid) 0) "orange" "gold")))
|
|
|
|
|
(propertize after-root 'face `(:foreground ,(if (= (user-uid) 0) "red" "green") :weight bold))
|
2017-11-01 18:22:05 +01:00
|
|
|
|
(or (magit-get-current-branch) (magit-get-current-tag))))
|
2017-11-01 17:03:47 +01:00
|
|
|
|
(format
|
|
|
|
|
(propertize "[%s]" 'face '(:weight bold))
|
|
|
|
|
(propertize path 'face `(:foreground ,(if (= (user-uid) 0) "red" "green") :weight bold))))
|
|
|
|
|
(propertize "\n>" 'face '(:weight bold))
|
2017-06-12 22:26:50 +02:00
|
|
|
|
" "))))
|
|
|
|
|
;;; If the prompt spans over multiple lines, the regexp should match
|
|
|
|
|
;;; last line only.
|
2017-06-18 20:52:16 +02:00
|
|
|
|
(setq-default eshell-prompt-regexp "^> ")
|
2014-02-12 18:31:00 +01:00
|
|
|
|
|
2017-06-16 20:53:25 +02:00
|
|
|
|
(with-eval-after-load 'em-term
|
2017-10-24 18:30:34 +02:00
|
|
|
|
(dolist (p '("abook" "alsamixer" "cmus" "dtach" "fzf" "htop" "mpsyt" "mpv" "mutt" "ncdu" "newsbeuter" "pinentry-curses" "ranger" "watch" "wifi-menu"))
|
|
|
|
|
(add-to-list 'eshell-visual-commands p))
|
2017-06-16 20:53:25 +02:00
|
|
|
|
(setq eshell-visual-subcommands
|
2017-10-24 18:30:34 +02:00
|
|
|
|
'(("git" "log" "diff" "show"
|
|
|
|
|
"l" "lol" "d" "dc") ; aliases
|
|
|
|
|
("sudo" "wifi-menu") ; Arch Linux
|
|
|
|
|
("sudo" "vi" "visudo"))))
|
2016-06-09 18:23:04 +02:00
|
|
|
|
|
2017-06-12 22:26:50 +02:00
|
|
|
|
;;; Alias management possibilities:
|
|
|
|
|
;;; - Version eshell-alias and store it in user-emacs-directory. Simplest and
|
|
|
|
|
;;; fastest, but aliases cannot be included conditionnaly, e.g. depending on the
|
|
|
|
|
;;; existence of a program.
|
|
|
|
|
;;; - Store eshell-alias in cache and populate it dynamically on startup.
|
|
|
|
|
;; (setq eshell-aliases-file (concat user-emacs-directory "eshell-alias"))
|
|
|
|
|
;;;
|
|
|
|
|
;;; `eshell/alias' is too slow as it reads and write the file on each definition.
|
|
|
|
|
;;; Let's write manually instead.
|
2017-06-16 20:53:25 +02:00
|
|
|
|
(with-eval-after-load 'em-alias
|
2017-06-18 22:25:56 +02:00
|
|
|
|
;;; TODO: This conflicts with `evil-define-key' during the initialization of
|
|
|
|
|
;;; the first eshell session: the map in insert-mode will not take the changes
|
|
|
|
|
;;; into account. Going to normal mode and back to insert mode works.
|
2017-10-21 11:35:03 +02:00
|
|
|
|
;;;
|
|
|
|
|
;;; If we read the alias list here, it means we make commandline-defined aliases persistent.
|
2017-10-24 18:30:34 +02:00
|
|
|
|
;; (eshell-read-aliases-list)
|
2017-06-16 20:53:25 +02:00
|
|
|
|
(dolist
|
|
|
|
|
(alias
|
|
|
|
|
'(("l" "ls -1 $*")
|
|
|
|
|
("la" "ls -lAh $*")
|
|
|
|
|
("ll" "ls -lh $*")
|
2017-10-23 21:06:08 +02:00
|
|
|
|
;; TODO: Aliasing eshell/{cp,mv,ln} does not work.
|
|
|
|
|
;; TODO: "sudo" does not work on aliases.
|
|
|
|
|
;; See bug #27168.
|
|
|
|
|
;; REVIEW: Eshell/TRAMP's sudo does not work with aliases.
|
|
|
|
|
;; See #28320.
|
2017-10-21 11:35:03 +02:00
|
|
|
|
;; ("ls" "ls -F $*") ; not supported
|
2017-06-16 20:53:25 +02:00
|
|
|
|
;; ("emacs" "find-file $1")
|
2017-10-23 21:06:08 +02:00
|
|
|
|
;; ("cp" "eshell/cp -iv $*")
|
2017-10-21 11:35:03 +02:00
|
|
|
|
;; ("mv" "eshell/mv -iv $*")
|
2017-10-23 21:06:08 +02:00
|
|
|
|
("cpv" "cp -iv $*")
|
2017-10-22 11:37:51 +02:00
|
|
|
|
("mvv" "mv -iv $*")
|
|
|
|
|
("rmv" "rm -v $*")
|
|
|
|
|
("md" "eshell/mkdir -p $*")
|
|
|
|
|
("mkcd" "eshell/mkdir -p $* && cd $1"))) ; TODO: Does not work because mkdir exits with nil?
|
2017-06-16 20:53:25 +02:00
|
|
|
|
(add-to-list 'eshell-command-aliases-list alias))
|
|
|
|
|
(eshell-write-aliases-list))
|
2014-02-12 18:31:00 +01:00
|
|
|
|
|
2017-06-17 13:14:45 +02:00
|
|
|
|
;;; Hooks
|
|
|
|
|
;;; `nobreak-char-display' makes some output look weird, e.g. with 'tree'.
|
|
|
|
|
(add-hook 'eshell-mode-hook 'turn-off-nobreak-char-display)
|
2017-06-18 20:58:21 +02:00
|
|
|
|
(add-hook 'eshell-mode-hook 'eshell-cmpl-initialize)
|
2017-06-17 13:14:45 +02:00
|
|
|
|
|
2017-10-22 21:19:12 +02:00
|
|
|
|
;;; History
|
2017-06-27 22:13:51 +02:00
|
|
|
|
;;; Filter out space-beginning commands from history.
|
2017-08-01 20:55:50 +02:00
|
|
|
|
;;; TODO: history/command hook: trim surrounding space. Check `eshell-rewrite-command-hook'.
|
|
|
|
|
;;; TODO: history: do not save failed commands to file.
|
2017-10-22 21:19:12 +02:00
|
|
|
|
;;; TODO: history: do not store duplicates. Push unique command to front of the list.
|
2017-06-27 22:13:51 +02:00
|
|
|
|
(setq eshell-input-filter
|
|
|
|
|
(lambda (str)
|
|
|
|
|
(not (or (string= "" str)
|
|
|
|
|
(string-prefix-p " " str)))))
|
|
|
|
|
|
2017-10-22 21:19:12 +02:00
|
|
|
|
;;; Shared history.
|
|
|
|
|
(defvar eshell-history-global-ring nil
|
|
|
|
|
"The history ring shared across Eshell sessions.")
|
|
|
|
|
|
|
|
|
|
(defun eshell-hist-use-global-history ()
|
|
|
|
|
"Make Eshell history shared across different sessions."
|
|
|
|
|
(unless eshell-history-global-ring
|
|
|
|
|
(let (eshell-history-ring)
|
|
|
|
|
(when eshell-history-file-name
|
|
|
|
|
(eshell-read-history nil t))
|
|
|
|
|
(setq eshell-history-global-ring eshell-history-ring))
|
|
|
|
|
(unless eshell-history-ring (setq eshell-history-global-ring (make-ring eshell-history-size))))
|
|
|
|
|
(setq eshell-history-ring eshell-history-global-ring))
|
|
|
|
|
(add-hook 'eshell-mode-hook 'eshell-hist-use-global-history)
|
|
|
|
|
|
|
|
|
|
;;; Spawning
|
2017-07-27 00:08:58 +02:00
|
|
|
|
(defun eshell-or-new-session (&optional arg)
|
|
|
|
|
"Create an interactive Eshell buffer.
|
2017-10-20 16:13:57 +02:00
|
|
|
|
Switch to last Eshell session if any.
|
|
|
|
|
Otherwise create a new one and switch to it.
|
2017-07-27 00:08:58 +02:00
|
|
|
|
See `eshell' for the numeric prefix ARG."
|
|
|
|
|
(interactive "P")
|
2017-10-20 16:13:57 +02:00
|
|
|
|
(if (or arg (eq major-mode 'eshell-mode))
|
2017-07-27 00:08:58 +02:00
|
|
|
|
(eshell (or arg t))
|
2017-10-20 16:13:57 +02:00
|
|
|
|
(let ((last (buffer-list)))
|
|
|
|
|
(while (and last
|
|
|
|
|
(not (with-current-buffer (car last)
|
|
|
|
|
(eq major-mode 'eshell-mode))))
|
|
|
|
|
(setq last (cdr last)))
|
|
|
|
|
(if last
|
|
|
|
|
(switch-to-buffer (car last))
|
|
|
|
|
(eshell (or arg t))))))
|
2017-07-27 00:08:58 +02:00
|
|
|
|
|
2017-11-01 16:06:15 +01:00
|
|
|
|
;;; Auto-suggestion
|
|
|
|
|
(when (require 'package-eshell-autosuggest)
|
|
|
|
|
(add-hook 'eshell-mode-hook 'company-mode)
|
|
|
|
|
(when (require 'helm-config nil t)
|
|
|
|
|
(define-key company-active-map (kbd "M-p") 'helm-eshell-history))
|
|
|
|
|
(add-hook 'eshell-mode-hook 'eshell-setup-autosuggest))
|
2017-07-07 18:38:00 +02:00
|
|
|
|
|
2017-11-01 17:59:53 +01:00
|
|
|
|
;;; Extra execution information
|
|
|
|
|
(defvar eshell-status-p t
|
|
|
|
|
"If non-nil, display status before prompt.")
|
|
|
|
|
(defvar eshell-status--last-command-time nil)
|
|
|
|
|
(make-variable-buffer-local 'eshell-status--last-command-time)
|
|
|
|
|
(defvar eshell-status-min-duration-before-display 1
|
|
|
|
|
"If a command takes more time than this, display its duration.")
|
|
|
|
|
|
|
|
|
|
(defun eshell-status-display ()
|
|
|
|
|
(when eshell-status--last-command-time
|
|
|
|
|
(let ((duration (time-subtract (current-time) eshell-status--last-command-time)))
|
2017-11-05 11:04:39 +01:00
|
|
|
|
(setq eshell-status--last-command-time nil)
|
2017-11-01 17:59:53 +01:00
|
|
|
|
(when (> (time-to-seconds duration) eshell-status-min-duration-before-display)
|
2017-11-02 23:35:14 +01:00
|
|
|
|
(format "#[STATUS] End time %s, duration %.3fs\n"
|
|
|
|
|
(format-time-string "%F %T" (current-time))
|
|
|
|
|
(time-to-seconds duration))))))
|
2017-11-01 17:59:53 +01:00
|
|
|
|
|
|
|
|
|
(defun eshell-status-record ()
|
|
|
|
|
(setq eshell-status--last-command-time (current-time)))
|
|
|
|
|
|
|
|
|
|
(add-hook 'eshell-pre-command-hook 'eshell-status-record)
|
|
|
|
|
|
2017-11-08 17:39:44 +01:00
|
|
|
|
;;; Detach
|
|
|
|
|
(when (require 'package-eshell-detach nil t)
|
|
|
|
|
(defun eshell-detach-set-keys ()
|
|
|
|
|
(define-key eshell-mode-map (kbd "S-<return>") 'eshell-detach-send-input))
|
|
|
|
|
(add-hook 'eshell-mode-hook 'eshell-detach-set-keys))
|
|
|
|
|
|
2017-07-07 18:38:00 +02:00
|
|
|
|
(provide 'init-eshell)
|