Moving to Doom Emacs!

This commit is contained in:
Derek Taylor
2019-12-16 20:21:19 -06:00
parent d9f2f456f1
commit d4b4c33550
683 changed files with 51877 additions and 100 deletions

View File

@@ -0,0 +1,30 @@
;;; term/eshell/autoload/commands.el -*- lexical-binding: t; -*-
;;;###autoload
(defun eshell/emacs (&rest files)
"Open a FILES in Emacs.
For folks with a habit of using \"emacs\" to open files, even in eshell."
(if files
(mapc #'find-file
(mapcar #'expand-file-name
(eshell-flatten-list (reverse files))))
(bury-buffer)))
;;;###autoload
(defalias 'eshell/e #'eshell/emacs)
;;;###autoload
(defun eshell/cd-to-project ()
"Change to the project root of the current directory."
(eshell/cd (doom-project-root (eshell/pwd))))
;;;###autoload
(defun eshell/quit-and-close (&rest _)
"Quit the current eshell buffer and close the window it's in."
(setq-local +eshell-kill-window-on-exit t)
(throw 'eshell-terminal t))
;;;###autoload
(defun eshell/mkdir-and-cd (dir)
"Create a directory then cd into it."
(make-directory dir t)
(eshell/cd dir))

View File

@@ -0,0 +1,58 @@
;;; term/eshell/autoload/company.el -*- lexical-binding: t; -*-
;;;###if (featurep! :completion company)
;; REVIEW Refactor me
(defvar company-pcomplete-available 'unknown)
(defun company-pcomplete--prefix ()
(let* ((pcomplete-stub)
pcomplete-seen
pcomplete-norm-func
pcomplete-args
pcomplete-last pcomplete-index
(pcomplete-autolist pcomplete-autolist)
(pcomplete-suffix-list pcomplete-suffix-list))
(pcomplete-completions)
(buffer-substring (pcomplete-begin) (point))))
(defun company-pcomplete--candidates ()
(let* ((pcomplete-stub)
(pcomplete-show-list t)
pcomplete-seen pcomplete-norm-func
pcomplete-args pcomplete-last pcomplete-index
(pcomplete-autolist pcomplete-autolist)
(pcomplete-suffix-list pcomplete-suffix-list)
(candidates (pcomplete-completions))
(prefix (buffer-substring (pcomplete-begin) (point)))
;; Collect all possible completions for the current stub
(cnds (all-completions pcomplete-stub candidates))
(bnds (completion-boundaries pcomplete-stub candidates nil ""))
(skip (- (length pcomplete-stub) (car bnds))))
;; Replace the stub at the beginning of each candidate by the prefix
(mapcar (lambda (cand)
(concat prefix (substring cand skip)))
cnds)))
;;;###autoload
(defun company-pcomplete-available ()
(when (eq company-pcomplete-available 'unknown)
(condition-case _err
(progn
(company-pcomplete--candidates)
(setq company-pcomplete-available t))
(error
(message "Company: pcomplete not found")
(setq company-pcomplete-available nil))))
company-pcomplete-available)
;;;###autoload
(defun company-pcomplete (command &optional _arg &rest ignored)
"`company-mode' completion backend using `pcomplete'."
(interactive (list 'interactive))
(cl-case command
(interactive (company-begin-backend 'company-pcomplete))
(prefix (when (company-pcomplete-available)
(company-pcomplete--prefix)))
(candidates (company-pcomplete--candidates))
(sorted t)))

View File

@@ -0,0 +1,323 @@
;;; term/eshell/autoload/eshell.el -*- lexical-binding: t; -*-
(defvar eshell-buffer-name "*doom:eshell*")
(defvar +eshell-buffers (make-ring 25)
"List of open eshell buffers.")
(defvar +eshell--last-buffer nil)
;;
;;; Helpers
(defun +eshell--add-buffer (buf)
(ring-remove+insert+extend +eshell-buffers buf 'grow))
(defun +eshell--remove-buffer (buf)
(when-let (idx (ring-member +eshell-buffers buf))
(ring-remove +eshell-buffers idx)
t))
(defun +eshell--bury-buffer (&optional dedicated-p)
(unless (switch-to-prev-buffer nil 'bury)
(switch-to-buffer (doom-fallback-buffer)))
(when (eq major-mode 'eshell-mode)
(switch-to-buffer (doom-fallback-buffer)))
(when +eshell-enable-new-shell-on-split
(when-let (win (get-buffer-window (+eshell/here)))
(set-window-dedicated-p win dedicated-p))))
(defun +eshell--setup-window (window &optional flag)
(when (window-live-p window)
(set-window-parameter window 'no-other-window flag)
(set-window-parameter window 'visible flag)))
(defun +eshell--unused-buffer (&optional new-p)
(or (unless new-p
(cl-loop for buf in (+eshell-buffers)
if (and (buffer-live-p buf)
(not (get-buffer-window buf t)))
return buf))
(generate-new-buffer eshell-buffer-name)))
;;;###autoload
(defun +eshell-last-buffer (&optional noerror)
"Return the last opened eshell buffer."
(let ((buffer (cl-find-if #'buffer-live-p (+eshell-buffers))))
(cond (buffer)
(noerror nil)
((user-error "No live eshell buffers remaining")))))
;;;###autoload
(defun +eshell-buffers ()
"TODO"
(ring-elements +eshell-buffers))
;;;###autoload
(defun +eshell-run-command (command &optional buffer)
"TODO"
(let ((buffer
(or buffer
(if (eq major-mode 'eshell-mode)
(current-buffer)
(cl-find-if #'buffer-live-p (+eshell-buffers))))))
(unless buffer
(user-error "No living eshell buffers available"))
(unless (buffer-live-p buffer)
(user-error "Cannot operate on a dead buffer"))
(with-current-buffer buffer
(goto-char eshell-last-output-end)
(goto-char (line-end-position))
(insert command)
(eshell-send-input nil t))))
;;
;;; Commands
;;;###autoload
(defun +eshell/toggle (arg &optional command)
"Toggle eshell popup window."
(interactive "P")
(let ((eshell-buffer
(get-buffer-create
(format "*doom:eshell-popup:%s*"
(if (bound-and-true-p persp-mode)
(safe-persp-name (get-current-persp))
"main"))))
confirm-kill-processes
current-prefix-arg)
(when arg
(when-let (win (get-buffer-window eshell-buffer))
(delete-window win))
(when (buffer-live-p eshell-buffer)
(with-current-buffer eshell-buffer
(fundamental-mode)
(erase-buffer))))
(if-let (win (get-buffer-window eshell-buffer))
(if (eq (selected-window) win)
(let (confirm-kill-processes)
(delete-window win)
(ignore-errors (kill-buffer eshell-buffer)))
(select-window win)
(when (bound-and-true-p evil-local-mode)
(evil-change-to-initial-state))
(goto-char (point-max)))
(with-current-buffer (pop-to-buffer eshell-buffer)
(doom-mark-buffer-as-real-h)
(if (eq major-mode 'eshell-mode)
(run-hooks 'eshell-mode-hook)
(eshell-mode))
(when command
(+eshell-run-command command eshell-buffer))))))
;;;###autoload
(defun +eshell/here (&optional command)
"Open eshell in the current buffer."
(interactive "P")
(let ((buf (+eshell--unused-buffer)))
(with-current-buffer (switch-to-buffer buf)
(if (eq major-mode 'eshell-mode)
(run-hooks 'eshell-mode-hook)
(eshell-mode))
(when command
(+eshell-run-command command buf)))
buf))
;;;###autoload
(defun +eshell/frame (&optional command)
"Open a frame dedicated to eshell.
Once the eshell process is killed, the previous frame layout is restored."
(interactive "P")
(let ((buf (+eshell--unused-buffer 'new)))
(unless (frame-parameter nil 'saved-wconf)
(set-frame-parameter nil 'saved-wconf (current-window-configuration)))
(delete-other-windows)
(with-current-buffer (switch-to-buffer buf)
(eshell-mode)
(when command
(+eshell-run-command command buf)))
buf))
;;
;;; Keybinds
;;;###autoload
(defun +eshell/search-history ()
"Search the eshell command history with helm, ivy or `eshell-list-history'."
(interactive)
(cond ((featurep! :completion ivy)
(require 'em-hist)
(let* ((ivy-completion-beg (eshell-bol))
(ivy-completion-end (point-at-eol))
(input (buffer-substring-no-properties
ivy-completion-beg
ivy-completion-end)))
;; Better than `counsel-esh-history' because that doesn't
;; pre-populate the initial input or selection.
(ivy-read "Command: "
(delete-dups
(when (> (ring-size eshell-history-ring) 0)
(ring-elements eshell-history-ring)))
:initial-input input
:action #'ivy-completion-in-region-action)))
((featurep! :completion helm)
(helm-eshell-history))
((eshell-list-history))))
;;;###autoload
(defun +eshell/pcomplete ()
"Use pcomplete with completion-in-region backend instead of popup window at
bottom. This ties pcomplete into ivy or helm, if they are enabled."
(interactive)
(require 'pcomplete)
(if (and (bound-and-true-p company-mode)
(or company-candidates
(and (company-pcomplete-available)
(company-pcomplete--prefix)
(company-pcomplete--candidates))))
(call-interactively #'company-pcomplete)
(ignore-errors (pcomplete-std-complete))))
;;;###autoload
(defun +eshell/quit-or-delete-char (arg)
"Delete a character (ahead of the cursor) or quit eshell if there's nothing to
delete."
(interactive "p")
(if (and (eolp) (looking-back eshell-prompt-regexp nil))
(eshell-life-is-too-much)
(delete-char arg)))
;;;###autoload
(defun +eshell/split-below ()
"Create a new eshell window below the current one."
(interactive)
(let ((ignore-window-parameters t)
(dedicated-p (window-dedicated-p))
(+eshell-enable-new-shell-on-split
(or +eshell-enable-new-shell-on-split (frame-parameter nil 'saved-wconf))))
(select-window (split-window-vertically))
(+eshell--bury-buffer dedicated-p)))
;;;###autoload
(defun +eshell/split-right ()
"Create a new eshell window to the right of the current one."
(interactive)
(let* ((ignore-window-parameters t)
(dedicated-p (window-dedicated-p))
(+eshell-enable-new-shell-on-split
(or +eshell-enable-new-shell-on-split (frame-parameter nil 'saved-wconf))))
(select-window (split-window-horizontally))
(+eshell--bury-buffer dedicated-p)))
;;;###autoload
(defun +eshell/switch-to-next ()
"Switch to the next eshell buffer."
(interactive)
(when (ring-empty-p +eshell-buffers)
(user-error "No eshell buffers are available"))
(switch-to-buffer (ring-next +eshell-buffers (current-buffer))))
;;;###autoload
(defun +eshell/switch-to-previous ()
"Switch to the previous eshell buffer."
(interactive)
(when (ring-empty-p +eshell-buffers)
(user-error "No eshell buffers are available"))
(switch-to-buffer (ring-previous +eshell-buffers (current-buffer))))
;;;###autoload
(defun +eshell/switch-to-last ()
"Switch to the last eshell buffer that was open (and is still alive)."
(interactive)
(unless (buffer-live-p +eshell--last-buffer)
(setq +eshell--last-buffer nil)
(user-error "No last eshell buffer to jump to"))
(switch-to-buffer +eshell--last-buffer))
;;;###autoload
(defun +eshell/switch-to (buffer)
"Interactively switch to another eshell buffer."
(interactive
(let ((buffers (doom-buffers-in-mode
'eshell-mode (delq (current-buffer) (+eshell-buffers)))))
(if (not buffers)
(user-error "No eshell buffers are available")
(list
(completing-read "Eshell buffers"
(mapcar #'buffer-name buffers)
#'get-buffer
'require-match
nil nil
(when (eq major-mode 'eshell-mode)
(buffer-name (current-buffer))))))))
(if-let* ((window (get-buffer-window buffer)))
(select-window window)
(switch-to-buffer buffer)))
;;;###autoload
(defun +eshell/kill-and-close ()
"Kill the current eshell buffer and close its window."
(interactive)
(unless (eq major-mode 'eshell-mode)
(user-error "Not in an eshell buffer"))
(let ((+eshell-kill-window-on-exit t))
(kill-current-buffer)))
;;
;;; Hooks
;;;###autoload
(defun +eshell-init-h ()
"Initialize and track this eshell buffer in `+eshell-buffers'."
(let ((current-buffer (current-buffer)))
(dolist (buf (+eshell-buffers))
(unless (buffer-live-p buf)
(+eshell--remove-buffer buf)))
(+eshell--setup-window (get-buffer-window current-buffer))
(+eshell--add-buffer current-buffer)
(setq +eshell--last-buffer current-buffer)))
;;;###autoload
(defun +eshell-cleanup-h ()
"Close window (or workspace) on quit."
(let ((buf (current-buffer)))
(when (+eshell--remove-buffer buf)
(when-let (win (get-buffer-window buf))
(+eshell--setup-window win nil)
(cond ((and (one-window-p t)
(window-configuration-p (frame-parameter nil 'saved-wconf)))
(set-window-configuration (frame-parameter nil 'saved-wconf))
(set-frame-parameter nil 'saved-wconf nil))
((one-window-p)
(let ((prev (save-window-excursion (previous-buffer))))
(unless (and prev (doom-real-buffer-p prev))
(switch-to-buffer (doom-fallback-buffer)))))
((or (window-dedicated-p win)
+eshell-kill-window-on-exit)
(let ((ignore-window-parameters t)
(popup-p (window-dedicated-p win)))
(delete-window win)
(when popup-p
(cl-loop for win in (window-list)
for buf = (window-buffer win)
for mode = (buffer-local-value 'major-mode buf)
if (eq mode 'eshell-mode)
return (select-window win))))))))))
;;;###autoload
(defun +eshell-switch-workspace-fn (type)
(when (eq type 'frame)
(setq +eshell-buffers
(or (persp-parameter 'eshell-buffers)
(make-ring 25)))))
;;;###autoload
(defun +eshell-save-workspace-fn (_workspace target)
(when (framep target)
(set-persp-parameter 'eshell-buffers +eshell-buffers)))

View File

@@ -0,0 +1,69 @@
;;; term/eshell/autoload/evil.el -*- lexical-binding: t; -*-
;;;###if (featurep! :editor evil)
;;;###autoload (autoload '+eshell:run "term/eshell/autoload/evil" nil t)
(evil-define-command +eshell:run (command bang)
"TODO"
(interactive "<fsh><!>")
(let ((buffer (+eshell-last-buffer))
(command (+evil-resolve-vim-path-a command)))
(cond (buffer
(select-window (get-buffer-window buffer))
(+eshell-run-command command buffer))
(bang (+eshell/open nil command))
((+eshell/open-popup nil command)))))
;;;###autoload
(defun +eshell-goto-prompt-on-insert-a ()
"Move cursor to the prompt when switching to insert mode (if point isn't
already there).
Meant to replace `evil-collection-eshell-next-prompt-on-insert'."
(when (< (point) eshell-last-output-end)
(goto-char
(if (memq this-command '(evil-append evil-append-line))
(point-max)
eshell-last-output-end))))
;;;###autoload
(defun +eshell/goto-end-of-prompt ()
"Move cursor to the prompt when switching to insert mode (if point isn't
already there)."
(interactive)
(goto-char (point-max))
(evil-append 1))
;;;###autoload (autoload '+eshell/evil-change "term/eshell/autoload/evil" nil t)
(evil-define-operator +eshell/evil-change (beg end type register yank-handler delete-func)
"Like `evil-change' but will not delete/copy the prompt."
(interactive "<R><x><y>")
(save-restriction
(narrow-to-region eshell-last-output-end (point-max))
(evil-change (max beg (point-min))
(if (eq type 'line) (point-max) (min (or end (point-max)) (point-max)))
type register yank-handler delete-func)))
;;;###autoload (autoload '+eshell/evil-change-line "term/eshell/autoload/evil" nil t)
(evil-define-operator +eshell/evil-change-line (beg end type register yank-handler)
"Change to end of line."
:motion evil-end-of-line
(interactive "<R><x><y>")
(+eshell/evil-change beg end type register yank-handler #'evil-delete-line))
;;;###autoload (autoload '+eshell/evil-delete "term/eshell/autoload/evil" nil t)
(evil-define-operator +eshell/evil-delete (beg end type register yank-handler)
"Like `evil-delete' but will not delete/copy the prompt."
(interactive "<R><x><y>")
(save-restriction
(narrow-to-region eshell-last-output-end (point-max))
(evil-delete (if beg (max beg (point-min)) (point-min))
(if (eq type 'line) (point-max) (min (or end (point-max)) (point-max)))
type register yank-handler)))
;;;###autoload (autoload '+eshell/evil-delete-line "term/eshell/autoload/evil" nil t)
(evil-define-operator +eshell/evil-delete-line (_beg end type register yank-handler)
"Change to end of line."
:motion nil
:keep-visual t
(interactive "<R><x>")
(+eshell/evil-delete (point) end type register yank-handler))

View File

@@ -0,0 +1,34 @@
;;; term/eshell/autoload/prompts.el -*- lexical-binding: t; -*-
;;;###autoload
(defface +eshell-prompt-pwd '((t (:inherit font-lock-constant-face)))
"TODO"
:group 'eshell)
;;;###autoload
(defface +eshell-prompt-git-branch '((t (:inherit font-lock-builtin-face)))
"TODO"
:group 'eshell)
(defun +eshell--current-git-branch ()
(cl-destructuring-bind (status . output)
(doom-call-process "git" "name-rev" "--name-only" "HEAD")
(if (equal status 0)
(format " [%s]" output)
"")))
;;;###autoload
(defun +eshell-default-prompt-fn ()
"Generate the prompt string for eshell. Use for `eshell-prompt-function'."
(require 'shrink-path)
(concat (if (bobp) "" "\n")
(let ((pwd (eshell/pwd)))
(propertize (if (equal pwd "~")
pwd
(abbreviate-file-name (shrink-path-file pwd)))
'face '+eshell-prompt-pwd))
(propertize (+eshell--current-git-branch)
'face '+eshell-prompt-git-branch)
(propertize " λ" 'face (if (zerop eshell-last-command-status) 'success 'error))
" "))

View File

@@ -0,0 +1,26 @@
;;; term/eshell/autoload/settings.el -*- lexical-binding: t; -*-
;;;###autodef
(defun set-eshell-alias! (&rest aliases)
"Define aliases for eshell.
ALIASES is a flat list of alias -> command pairs. e.g.
(set-eshell-alias!
\"hi\" \"echo hello world\"
\"bye\" \"echo goodbye world\")"
(or (cl-evenp (length aliases))
(signal 'wrong-number-of-arguments (list 'even (length aliases))))
(after! eshell
(while aliases
(let ((alias (pop aliases))
(command (pop aliases)))
(if-let* ((oldval (assoc alias +eshell-aliases)))
(setcdr oldval (list command))
(push (list alias command) +eshell-aliases))))
(when (boundp 'eshell-command-aliases-list)
(if +eshell--default-aliases
(setq eshell-command-aliases-list
(append +eshell--default-aliases
+eshell-aliases))
(setq eshell-command-aliases-list +eshell-aliases)))))

View File

@@ -0,0 +1,180 @@
;;; term/eshell/config.el -*- lexical-binding: t; -*-
;; see:
;; + `+eshell/here': open eshell in the current window
;; + `+eshell/toggle': toggles an eshell popup
;; + `+eshell/frame': converts the current frame into an eshell-dedicated
;; frame. Once the last eshell process is killed, the old frame configuration
;; is restored.
(defvar +eshell-config-dir
(expand-file-name "eshell/" doom-private-dir)
"Where to store eshell configuration files, as opposed to
`eshell-directory-name', which is where Doom will store temporary/data files.")
(defvar +eshell-enable-new-shell-on-split t
"If non-nil, spawn a new eshell session after splitting from an eshell
buffer.")
(defvar +eshell-kill-window-on-exit nil
"If non-nil, eshell will close windows along with its eshell buffers.")
(defvar +eshell-aliases
'(("q" "exit") ; built-in
("f" "find-file $1")
("d" "dired $1")
("bd" "eshell-up $1")
("rg" "rg --color=always $*")
("l" "ls -lh")
("ll" "ls -lah")
("clear" "clear-scrollback")) ; more sensible than default
"An alist of default eshell aliases, meant to emulate useful shell utilities,
like fasd and bd. Note that you may overwrite these in your
`eshell-aliases-file'. This is here to provide an alternative, elisp-centric way
to define your aliases.
You should use `set-eshell-alias!' to change this.")
;;
(defvar eshell-directory-name (concat doom-etc-dir "eshell"))
;; These files are exceptions, because they may contain configuration
(defvar eshell-aliases-file (concat +eshell-config-dir "aliases"))
(defvar eshell-rc-script (concat +eshell-config-dir "profile"))
(defvar eshell-login-script (concat +eshell-config-dir "login"))
(defvar +eshell--default-aliases nil)
;;
;; Packages
(after! eshell ; built-in
(setq eshell-banner-message
'(format "%s %s\n"
(propertize (format " %s " (string-trim (buffer-name)))
'face 'mode-line-highlight)
(propertize (current-time-string)
'face 'font-lock-keyword-face))
eshell-scroll-to-bottom-on-input 'all
eshell-scroll-to-bottom-on-output 'all
eshell-buffer-shorthand t
eshell-kill-processes-on-exit t
eshell-hist-ignoredups t
;; don't record command in history if prefixed with whitespace
;; TODO Use `eshell-input-filter-initial-space' when Emacs 25 support is dropped
eshell-input-filter (lambda (input) (not (string-match-p "\\`\\s-+" input)))
;; em-prompt
eshell-prompt-regexp "^.* λ "
eshell-prompt-function #'+eshell-default-prompt-fn
;; em-glob
eshell-glob-case-insensitive t
eshell-error-if-no-glob t)
;; Consider eshell buffers real
(add-hook 'eshell-mode-hook #'doom-mark-buffer-as-real-h)
;; Keep track of open eshell buffers
(add-hook 'eshell-mode-hook #'+eshell-init-h)
(add-hook 'eshell-exit-hook #'+eshell-cleanup-h)
;; Enable autopairing in eshell
(add-hook 'eshell-mode-hook #'smartparens-mode)
;; Persp-mode/workspaces integration
(when (featurep! :ui workspaces)
(add-hook 'persp-activated-functions #'+eshell-switch-workspace-fn)
(add-hook 'persp-before-switch-functions #'+eshell-save-workspace-fn))
;; UI enhancements
(add-hook! 'eshell-mode-hook
(defun +eshell-remove-fringes-h ()
(set-window-fringes nil 0 0)
(set-window-margins nil 1 nil)))
(add-hook! 'eshell-mode-hook
(defun +eshell-enable-text-wrapping-h ()
(visual-line-mode +1)
(set-display-table-slot standard-display-table 0 ?\ )))
(add-hook 'eshell-mode-hook #'hide-mode-line-mode)
;; Don't auto-write our aliases! Let us manage our own `eshell-aliases-file'
;; or configure `+eshell-aliases' via elisp.
(advice-add #'eshell-write-aliases-list :override #'ignore)
;; Visual commands require a proper terminal. Eshell can't handle that, so
;; it delegates these commands to a term buffer.
(after! em-term
(pushnew! eshell-visual-commands "tmux" "htop" "vim" "nvim" "ncmpcpp"))
(add-hook! 'eshell-alias-load-hook
(defun +eshell-init-aliases-h ()
(setq +eshell--default-aliases eshell-command-aliases-list
eshell-command-aliases-list
(append eshell-command-aliases-list
+eshell-aliases))))
(when (featurep! :editor evil +everywhere)
(advice-add #'evil-collection-eshell-next-prompt-on-insert
:override #'+eshell-goto-prompt-on-insert-a))
(add-hook! 'eshell-first-time-mode-hook
(defun +eshell-init-keymap-h ()
;; Keys must be bound in a hook because eshell resets its keymap every
;; time `eshell-mode' is enabled. Why? It is not for us mere mortals to
;; grasp such wisdom.
(map! :map eshell-mode-map
:n "RET" #'+eshell/goto-end-of-prompt
:n [return] #'+eshell/goto-end-of-prompt
:n "c" #'+eshell/evil-change
:n "C" #'+eshell/evil-change-line
:n "d" #'+eshell/evil-delete
:n "D" #'+eshell/evil-delete-line
:ig "C-d" #'+eshell/quit-or-delete-char
"TAB" #'+eshell/pcomplete
[tab] #'+eshell/pcomplete
"C-s" #'+eshell/search-history
;; Emacs bindings
"C-e" #'end-of-line
;; Tmux-esque prefix keybinds
"C-c s" #'+eshell/split-below
"C-c v" #'+eshell/split-right
"C-c x" #'+eshell/kill-and-close
"C-c h" #'windmove-left
"C-c j" #'windmove-down
"C-c k" #'windmove-up
"C-c l" #'windmove-right
[remap split-window-below] #'+eshell/split-below
[remap split-window-right] #'+eshell/split-right
[remap doom/backward-to-bol-or-indent] #'eshell-bol
[remap doom/backward-kill-to-bol-and-indent] #'eshell-kill-input
[remap evil-window-split] #'+eshell/split-below
[remap evil-window-vsplit] #'+eshell/split-right)))
(add-hook! 'eshell-mode-hook
(defun +eshell-init-company-h ()
(when (featurep! :completion company)
(company-mode +1)
(setq-local company-backends '(company-pcomplete))
(setq-local company-frontends (cons 'company-tng-frontend company-frontends))
(when (bound-and-true-p evil-local-mode)
(evil-normalize-keymaps))))))
(use-package! eshell-up
:commands eshell-up eshell-up-peek)
(use-package! eshell-z
:after eshell
:config
;; Use zsh's db if it exists, otherwise, store it in `doom-cache-dir'
(unless (file-exists-p eshell-z-freq-dir-hash-table-file-name)
(setq eshell-z-freq-dir-hash-table-file-name
(expand-file-name "z" eshell-directory-name))))
(use-package! esh-help
:after eshell
:config (setup-esh-help-eldoc))

View File

@@ -0,0 +1,7 @@
;; -*- no-byte-compile: t; -*-
;;; term/eshell/packages.el
(package! eshell-up)
(package! eshell-z)
(package! shrink-path)
(package! esh-help)

View File

@@ -0,0 +1,100 @@
;;; term/shell/autoload.el -*- lexical-binding: t; -*-
(defun +shell-idle-p (buf)
"Return t if the shell in BUF is not running something.
When available, use process hierarchy information via pstree for
local shells. Otherwise, we ask comint if the point is after a
prompt."
(with-current-buffer buf
(let ((comint-says-idle (and
(> (point) 1) ;; if point > 1
;; see if previous char has the prompt face
(equal '(comint-highlight-prompt)
(get-text-property
(- (point) 1) 'font-lock-face)))))
(if (file-remote-p default-directory)
;; for remote shells we have to rely on comint
comint-says-idle
;; for local shells, we can potentially do better using pgrep
(condition-case nil
(case (call-process ;; look at the exit code of pgrep -P <pid>
"pgrep" nil nil nil "-P"
(number-to-string (process-id (get-buffer-process buf))))
(0 nil) ;; child procxesses found, not idle
(1 t) ;; not running any child processes, it's idle
(t comint-says-idle)) ;; anything else, fall back on comint.
(error comint-says-idle)))))) ;; comint fallback if execution failed
(defun +shell-unused-buffer ()
"TODO"
(or (cl-find-if #'+shell-idle-p (doom-buffers-in-mode 'shell-mode))
(generate-new-buffer "*doom:shell*")))
(defun +shell-tramp-hosts ()
"Ask tramp for a list of hosts that we can reach through ssh."
(cl-reduce #'append
(mapcar (lambda (x)
(delq nil (mapcar #'cadr (apply (car x) (cdr x)))))
(tramp-get-completion-function "scp"))))
(defun +shell--sentinel (process _event)
(when (memq (process-status process) '(exit stop))
(kill-buffer (process-buffer process))))
;;;###autoload
(defun +shell/toggle (&optional command)
"Toggle a persistent terminal popup window.
If popup is visible but unselected, selected it.
If popup is focused, kill it."
(interactive)
(let ((buffer
(get-buffer-create
(format "*doom:shell-popup:%s*"
(if (bound-and-true-p persp-mode)
(safe-persp-name (get-current-persp))
"main"))))
(dir default-directory))
(if-let (win (get-buffer-window buffer))
(if (eq (selected-window) win)
(let (confirm-kill-processes)
(set-process-query-on-exit-flag (get-buffer-process buffer) nil)
(delete-window win)
(ignore-errors (kill-buffer buffer)))
(select-window win)
(when (bound-and-true-p evil-local-mode)
(evil-change-to-initial-state))
(goto-char (point-max)))
(with-current-buffer (pop-to-buffer buffer)
(if (not (eq major-mode 'shell-mode))
(shell buffer)
(run-mode-hooks 'shell-mode-hook)
(cd dir))
(let ((process (get-buffer-process (current-buffer))))
(set-process-sentinel process #'+shell--sentinel)
(when command
(comint-send-string process command)))))))
;;;###autoload
(defun +shell/here (&optional command)
"Open a terminal buffer in the current window.
If already in a shell buffer, clear it and cd into the current directory."
(interactive)
(let ((buffer (+shell-unused-buffer))
(dir default-directory))
(with-current-buffer (switch-to-buffer buffer)
(if (not (eq major-mode 'shell-mode))
(shell buffer)
(erase-buffer)
(cd dir))
(let ((process (get-buffer-process (current-buffer))))
(set-process-sentinel process #'+shell--sentinel)
(when command
(comint-send-string process command))))
buffer))
;; TODO +shell/frame -- dedicate current frame to shell buffers
;; TODO +shell/frame-quite -- revert frame to before +term/frame

View File

@@ -0,0 +1,5 @@
;;; term/shell/config.el -*- lexical-binding: t; -*-
;;;###package shell
(add-hook 'shell-mode-hook #'doom-mark-buffer-as-real-h)
(add-hook 'shell-mode-hook #'hide-mode-line-mode)

View File

@@ -0,0 +1,4 @@
;; -*- no-byte-compile: t; -*-
;;; term/shell/packages.el
(package! shell :built-in t)

View File

@@ -0,0 +1,52 @@
;;; term/term/autoload.el -*- lexical-binding: t; -*-
(defun +term--kill-dedicated (window buffer)
(when (window-live-p window)
(delete-window window))
(when (buffer-live-p buffer)
(set-process-query-on-exit-flag (get-buffer-process buffer) nil)
(kill-buffer buffer)))
;;;###autoload
(defun +term/toggle (arg)
"Toggle a persistent terminal popup window.
If popup is visible but unselected, select it.
If prefix ARG, recreate the term buffer."
(interactive "P")
(require 'multi-term)
(let ((multi-term-dedicated-select-after-open-p t)
(multi-term-dedicated-buffer-name
(format "doom:term-popup:%s"
(if (bound-and-true-p persp-mode)
(safe-persp-name (get-current-persp))
"main"))))
(let* ((buffer (multi-term-get-buffer nil t))
(window (get-buffer-window buffer)))
(when arg
(+term--kill-dedicated window buffer)
(setq buffer (multi-term-get-buffer nil t))) ; recreates buffer
(if (and (window-live-p window)
(buffer-live-p buffer))
(if (eq (selected-window) window)
(delete-window window)
(select-window window)
(when (bound-and-true-p evil-local-mode)
(evil-change-to-initial-state))
(goto-char (point-max)))
(setenv "PROOT" (or (doom-project-root) default-directory))
(with-current-buffer buffer
(doom-mark-buffer-as-real-h)
(multi-term-internal))
(unless (window-live-p window)
(when-let (window
(display-buffer-in-side-window
buffer `((window-height . ,multi-term-dedicated-window-height))))
(select-window window)))))))
;;;###autoload
(defalias '+term/here #'multi-term)
;; TODO +term/frame -- dedicate current frame to term buffers
;; TODO +term/frame-quite -- revert frame to before +term/frame

View File

@@ -0,0 +1,10 @@
;;; term/term/config.el -*- lexical-binding: t; -*-
;;;###package term
(add-hook 'term-mode-hook #'doom-mark-buffer-as-real-h)
(add-hook 'term-mode-hook #'hide-mode-line-mode)
;;;###package multi-term
(setq multi-term-dedicated-window-height 20
multi-term-switch-after-close 'PREVIOUS)

View File

@@ -0,0 +1,5 @@
;; -*- no-byte-compile: t; -*-
;;; term/term/packages.el
(package! term :built-in t)
(package! multi-term)

View File

@@ -0,0 +1,108 @@
#+TITLE: term/vterm
#+DATE: January 16, 2019
#+SINCE: 2.1
#+STARTUP: inlineimages
* Table of Contents :TOC_3:noexport:
- [[#description][Description]]
- [[#module-flags][Module Flags]]
- [[#plugins][Plugins]]
- [[#prerequisites][Prerequisites]]
- [[#dynamic-module-support][Dynamic Module support]]
- [[#libvterm][libvterm]]
- [[#compilation-tools-for-vterm-moduleso][Compilation tools for vterm-module.so]]
* Description
This module provides a terminal emulator powered by libvterm. It is still in
alpha and requires a component be compiled (=vterm-module.sh=).
The following commands are available to open it:
+ ~+vterm/toggle~ (=SPC o t=): Toggle vterm pop up window in the current project
+ ~+vterm/here~ (=SPC o T=): Opens vterm in the current window
** Module Flags
This module provides no flags.
** Plugins
+ [[https://github.com/akermu/emacs-libvterm][vterm]]
* Prerequisites
+ Emacs must be built with dynamic module support, i.e. compiled with the
=--with-modules= option.
+ You need =libvterm= installed on your system.
+ You need =make=, =cmake= and a C compiler such as =gcc= so that vterm can
build =vterm-module.so=.
** Dynamic Module support
To check if your build of Emacs was built with dynamic module support, check
~bin/doom info~ for ~MODULES~ next to "System features". If it's there, you're
good to go.
You can also check for =--with-modules= in the ~system-configuration-options~
variable (=SPC h v system-configuration-options=).
- Archlinux or Manjaro users who installed Emacs through pacman will have
support baked in.
- MacOS users:
- If you use [[https://emacsformacosx.com/][Emacs For Mac OS X]], this option is enabled.
- If you use [[https://github.com/d12frosted/homebrew-emacs-plus][emacs-plus]], this option is enabled by default.
- If you use [[https://github.com/railwaycat/homebrew-emacsmacport][emacs-mac]], this options is *not* enabled by default. You may have
to reinstall emacs with the option: ~brew install emacs-mac --with-modules~
** libvterm
+ Ubuntu or Debian users: ~apt-get install libvterm-dev~
+ ArchLinux or Manjaro: ~pacman -S libvterm~
+ MacOS: ~libvterm~
+ NixOS:
#+BEGIN_SRC nix
systemPackages = with pkgs; [
# emacs # no need for this, the next line includes emacs
((emacsPackagesNgGen emacs).emacsWithPackages (epkgs: [
epkgs.emacs-libvterm
]))
];
#+END_SRC
Or for home-manager users:
#+BEGIN_SRC nix
programs.emacs = {
enable = true;
extraPackages = epkgs: [ epkgs.emacs-libvterm ];
};
#+END_SRC
** Compilation tools for vterm-module.so
When you first load vterm, it will compile =vterm-module.so= for you. For this
to succeed, you need the following:
+ =make=
+ =cmake=
+ A C compiler like =gcc=
+ An internet connection (=cmake= will download needed libraries)
There are several ways to manually install the module:
1. You can use =M-x vterm-module-compile= to let emacs automatically compile and
install the module.
*WARNING*: Emacs will hang during the compilation. It may take a while.
2. You can compile and install the module yourself. Go to the vterm installation
directory (usually =~/.emacs.d/.local/packages/elpa/vterm-<version>=) and run
the following:
#+BEGIN_SRC sh
mkdir -p build
cd build
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
make
#+END_SRC
3. You can also compile =vterm-module.sh= elsewhere, but the module must be
moved/symlinked to
=~/.emacs.d/.local/packages/elpa/vterm-<version>/vterm-module.so=
=vterm-module.so=. Keep in mind that this folder will be deleted whenever the
vterm package is updated.

View File

@@ -0,0 +1,57 @@
;;; term/vterm/autoload.el -*- lexical-binding: t; -*-
;;;###autoload
(defun +vterm/toggle (arg)
"Toggles a terminal popup window at project root.
If prefix ARG is non-nil, recreate vterm buffer in the current project's root."
(interactive "P")
(unless (fboundp 'module-load)
(user-error "Your build of Emacs lacks dynamic modules support and cannot load vterm"))
(let ((buffer-name
(format "*doom:vterm-popup:%s*"
(if (bound-and-true-p persp-mode)
(safe-persp-name (get-current-persp))
"main")))
confirm-kill-processes
current-prefix-arg)
(when arg
(let ((buffer (get-buffer buffer-name))
(window (get-buffer-window buffer-name)))
(when (buffer-live-p buffer)
(kill-buffer buffer))
(when (window-live-p window)
(delete-window window))))
(if-let (win (get-buffer-window buffer-name))
(if (eq (selected-window) win)
(delete-window win)
(select-window win)
(when (bound-and-true-p evil-local-mode)
(evil-change-to-initial-state))
(goto-char (point-max)))
(require 'vterm)
(setenv "PROOT" (or (doom-project-root) default-directory))
(let ((buffer (get-buffer-create buffer-name)))
(with-current-buffer buffer
(doom-mark-buffer-as-real-h)
(unless (eq major-mode 'vterm-mode)
(vterm-mode)))
(pop-to-buffer buffer)))))
;;;###autoload
(defun +vterm/here (arg)
"Open a terminal buffer in the current window at project root.
If prefix ARG is non-nil, cd into `default-directory' instead of project root."
(interactive "P")
(unless (fboundp 'module-load)
(user-error "Your build of Emacs lacks dynamic modules support and cannot load vterm"))
(require 'vterm)
;; This hack forces vterm to redraw, fixing strange artefacting in the tty.
(save-window-excursion
(pop-to-buffer "*scratch*"))
(let ((default-directory
(if arg
default-directory
(or (doom-project-root) default-directory))))
(vterm)))

View File

@@ -0,0 +1,18 @@
;;; term/vterm/config.el -*- lexical-binding: t; -*-
(use-package! vterm
:when module-file-suffix
:defer t
:preface (setq vterm-install t)
:config
(set-popup-rule! "^vterm" :size 0.25 :vslot -4 :select t :quit nil :ttl 0)
(add-hook 'vterm-mode-hook #'doom-mark-buffer-as-real-h)
;; Automatically kill buffer when vterm exits.
(add-hook! 'vterm-exit-functions
(defun +vterm-kill-buffer-on-quit-fn (buffer event)
(if buffer (kill-buffer buffer))))
;; Modeline serves no purpose in vterm
(add-hook 'vterm-mode-hook #'hide-mode-line-mode)
;; Don't prompt about processes when killing vterm
(setq-hook! 'vterm-mode-hook confirm-kill-processes nil))

View File

@@ -0,0 +1,13 @@
;;; term/vterm/doctor.el -*- lexical-binding: t; -*-
(unless (executable-find "vterm-ctrl")
(warn! "Couldn't find libvterm. Vterm module won't compile"))
(unless (executable-find "make")
(warn! "Couldn't find make command. Vterm module won't compile"))
(unless (executable-find "cmake")
(warn! "Couldn't find cmake command. Vterm module won't compile"))
(unless (fboundp 'module-load)
(warn! "Your emacs wasn't built with dynamic modules support. The vterm module won't build"))

View File

@@ -0,0 +1,4 @@
;; -*- no-byte-compile: t; -*-
;;; term/vterm/packages.el
(package! vterm :built-in 'prefer)