Emacs: Tweak Eshell (prompt, aliases, evil bindings, next-prompt defun, helm)

master
Pierre Neidhardt 2017-06-12 21:26:50 +01:00
parent e44a13c3e5
commit c37af9b279
4 changed files with 124 additions and 28 deletions

View File

@ -236,6 +236,7 @@ e-mail."
(add-hook 'dired-mode-hook (lambda () (require 'mode-dired)))
;;; Eshell
(add-to-list 'package-selected-packages 'pcomplete-extension)
(add-hook 'eshell-load-hook (lambda () (require 'mode-eshell)))
;;; Evil

View File

@ -1,47 +1,87 @@
;; Eshell
;;; Eshell
;; This mode has a lot of hooks.
;; `emacs-load-hook' is run at the very beginning; not all variables/functions will be set.
;; `emacs-mode-hook' is run once everything is loaded.
;;; This mode has a lot of hooks.
;;; `emacs-load-hook' is run at the very beginning; not all variables/functions will be set.
;;; `emacs-mode-hook' is run once everything is loaded.
(setq eshell-directory-name (concat emacs-cache-folder "eshell"))
;;; TODO: Bind "ls"? No need if we have Ctrl-e?
;; (local-set-key "\C-l" 'eshell/ls)
;; 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"))
(setq
eshell-directory-name (concat emacs-cache-folder "eshell")
eshell-banner-message ""
eshell-ls-use-colors t
eshell-destroy-buffer-when-process-dies t
;; TODO: Hour is printed twice. We don't need to set this?
;; eshell-ls-date-format (replace-regexp-in-string "^\\+*" "" (getenv "TIME_STYLE"))
)
;;; Leave `eshell-highlight-prompt' to t as it sets the read-only property.
(setq eshell-prompt-function
(lambda nil
(let ((path (abbreviate-file-name (eshell/pwd))))
(format "(%s@%s)[%s]\n%s "
(user-login-name)
(system-name)
path
(if (= (user-uid) 0) "#" ">")))))
;; If the prompt spans over multiple lines, the regexp should match last line only.
(setq eshell-prompt-regexp "^[#>] ")
(concat
(format
(propertize "(%s@%s)[%s]\n>" 'face '(:weight bold))
(propertize (user-login-name) 'face '(:foreground "cyan"))
(propertize (system-name) 'face '(:foreground "cyan"))
(propertize path 'face `(:foreground ,(if (= (user-uid) 0) "red" "green") :weight bold)))
" "))))
;;; If the prompt spans over multiple lines, the regexp should match
;;; last line only.
(setq eshell-prompt-regexp "^> ")
(with-eval-after-load 'em-term
(nconc eshell-visual-commands
'("abook" "cmus" "fzf" "htop" "mpv" "mutt" "ncdu" "newsbeuter" "ranger")))
;; (with-eval-after-load 'em-term
;; (add-to-list 'eshell-visual-subcommands '("git" "log" "diff" "show")))
'("abook" "cmus" "fzf" "htop" "mpv" "mutt" "ncdu" "newsbeuter" "ranger"))
(add-to-list 'eshell-visual-subcommands '("git" "log" "diff" "show")))
;; eshell/alias is too slow as it reads and write the file on each definition.
;;; 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.
;;; TODO: Add pacman functions from fish config.
;;; TODO: Compare system tools and lisp equivalents of ls and grep.
(with-eval-after-load 'em-alias
(eshell-read-aliases-list)
(dolist (alias '(("ls" "ls -F $*")
(dolist (alias '(
;; ("ls" "ls -F $*")
("l" "ls -1 $*")
("la" "ls -lAh $*")
("ll" "ls -lh $*")
("grep" "grep --color=auto")
;; ("grep" "grep --color=auto")
("cal" "cal -m $*")
("cp" "cp -i $*")
("mv" "mv -i $*")
("mkdir" "mkdir -p $*")
("mkcd" "mkdir -p $* && cd $1")
("emacs" "find-file $1")
;; ("emacs" "find-file $1")
("em" "find-file $1")))
(add-to-list 'eshell-command-aliases-list alias))
(eshell-write-aliases-list))
;;; Emacs' standard function fail when output has empty lines.
;;; This implementation is more reliable.
;;; TODO: Report upstream
(defun eshell-next-prompt (n)
"Move to end of Nth next prompt in the buffer.
See `eshell-prompt-regexp'."
(interactive "p")
(re-search-forward eshell-prompt-regexp nil t n)
(while (not (get-text-property (line-beginning-position) 'read-only) )
(re-search-forward eshell-prompt-regexp nil t n))
(eshell-skip-prompt))
(defun eshell-previous-prompt (n)
"Move to end of Nth previous prompt in the buffer.
See `eshell-prompt-regexp'."
(interactive "p")
(backward-char)
(eshell-next-prompt (- n)))
(provide 'mode-eshell)

View File

@ -31,15 +31,14 @@
(evil-mode 1)
(evil-leader/set-key
"RET" 'spawn-terminal
"RET" 'eshell
"\\" 'toggle-window-split
"b" 'buffer-menu
"e" 'find-file
"k" 'kill-this-buffer
"o" 'delete-other-windows
"w" 'evil-window-next
"|" 'swap-windows
)
"|" 'swap-windows)
(when (require 'magit nil t)
;; Use S-SPC instead of SPC to browse commit details.
(evil-leader/set-key "v" 'magit-status))
@ -159,6 +158,54 @@
(evil-define-key 'normal package-menu-mode-map "d" 'package-menu-mark-delete)
(evil-define-key 'normal package-menu-mode-map "x" 'package-menu-execute)
;; Eshell
(defun evil/eshell-insert ()
(interactive)
(when (get-text-property (point) 'read-only)
(eshell-next-prompt 1))
(evil-insert 1))
(defun evil/eshell-interrupt-process ()
(interactive)
(eshell-interrupt-process)
(evil-insert 1))
(defun evil/eshell-define-keys ()
(with-eval-after-load 'tool-helm
(evil-define-key '(normal insert) eshell-mode-map "\C-r" 'helm-eshell-history)
(evil-define-key 'insert eshell-mode-map "\C-e" 'helm-find-files))
(evil-define-key 'normal eshell-mode-map "i" 'evil/eshell-insert)
(evil-define-key 'normal eshell-mode-map "\M-k" 'eshell-previous-prompt)
(evil-define-key 'normal eshell-mode-map "\M-j" 'eshell-next-prompt)
(evil-define-key 'normal eshell-mode-map "0" 'eshell-bol)
(evil-define-key 'normal eshell-mode-map (kbd "RET") 'eshell-send-input)
(evil-define-key 'normal eshell-mode-map (kbd "C-c C-c") 'evil/eshell-interrupt-process)
(evil-define-key '(normal insert) eshell-mode-map "\M-h" 'eshell-backward-argument)
(evil-define-key '(normal insert) eshell-mode-map "\M-l" 'eshell-forward-argument))
(add-hook 'eshell-mode-hook 'evil/eshell-define-keys)
;; TODO: When point is on "> ", helm-find-files looks up parent folder. Prevent that.
;; DONE: eshell-mode-map gets reset on new shells. Make it permanent. Hook? Hook looks good:
;; https://stackoverflow.com/questions/11946113/emacs-eshell-how-to-read-content-of-command-line-on-pressing-ret
;; TODO: Cannot kill emacs when eshell has started: "text is read only"
;; TODO: Make Evil commands react more dynamically with read-only text.
;; Add support for I, C, D, S, s, c*, d*, R, r.
(defun evil/eshell-delete-whole-line ()
(interactive)
(if (not (get-text-property (line-beginning-position) 'read-only))
(evil-delete-whole-line (line-beginning-position) (line-end-position))
(eshell-return-to-prompt) ; Difference with eshell-bol?
(evil-delete-line (point) (line-end-position))))
;; (evil-define-key 'normal eshell-mode-map "dd" 'evil/eshell-delete-whole-line)
(defun evil/eshell-change-whole-line ()
(interactive)
(if (not (get-text-property (line-beginning-position) 'read-only))
(evil-change-whole-line (line-beginning-position) (line-end-position))
(eshell-return-to-prompt) ; Difference with eshell-bol?
(evil-change-line (point) (line-end-position))))
;; (evil-define-key 'normal eshell-mode-map "cc" 'evil/eshell-change-whole-line)
;; Go-to-definition.
;; From https://emacs.stackexchange.com/questions/608/evil-map-keybindings-the-vim-way
(evil-global-set-key

View File

@ -54,6 +54,14 @@
(helm-make-source "Git files" 'helm-ls-git-source
:fuzzy-match helm-ls-git-fuzzy-match)))
;;; Eshell
(add-hook
'eshell-mode-hook
(lambda ()
(eshell-cmpl-initialize)
(define-key eshell-mode-map [remap eshell-pcomplete] 'helm-esh-pcomplete)
(define-key eshell-mode-map (kbd "C-r") 'helm-eshell-history)))
;;; Do not exclude any files from 'git grep'.
(setq helm-grep-git-grep-command "git --no-pager grep -n%cH --color=always --full-name -e %p -- %f")