Emacs As Your Terminal Emulator

Emacs As Your Terminal Emulator [Video]

  • Emacs offers an enhanced experience for interacting with shell tools & may be the most extensible terminal emulator available.
  • Provides seamless integration between shell tools & editing capabilities.

Using shell commands from within a buffer

shell-command

  • M-! CMD <RET>
  • Run the shell command CMD and display the output.

async-shell-command

  • M-& CMD <RET>
  • Run the shell command CMD asynchronously, and display the output

shell-command-on-region

  • M-| CMD <RET>
  • Run the shell command CMD with region contents as input; optionally replace the region with the output

Emacs as a Terminal Emulator

Shell

  • M-x shell
  • A subshell with input and output through an Emacs buffer. You can then give commands interactively.

My configuration

(use-package shell
  :config
  (defun thanos/shell (n)
    "Create or switch to a shell buffer."
    (interactive "P")
    (let* ((num (if n (prefix-numeric-value n) nil))
           (buf-name (if num (format "*shell<%d>*" num) "*shell*")))
      (shell buf-name)))
  :bind (("C-c v" . thanos/shell)
         :map shell-mode-map
         ("C-l" . 'comint-clear-buffer))
  :hook ((shell-mode . (lambda () (display-line-numbers-mode -1)))))

Eshell

  • M-x eshell
  • Shell implemented entirely in Emacs Lisp

My configuration

(use-package eshell
  :config
  ;; Prompt
  (defun eshell-git-info ()
    "Return a string with git info."
    (when (eq (call-process "git" nil nil nil "rev-parse" "--is-inside-work-tree") 0)
      (let* ((branch-raw (shell-command-to-string "git rev-parse --abbrev-ref HEAD"))
             (branch (if (or (string-match-p "^fatal" branch-raw)
                             (string-match-p "^error" branch-raw))
                         "Unknown"
                       (string-trim branch-raw)))
             (dirty (not
                     (string= "" (string-trim (shell-command-to-string "git status --porcelain")))))
             (dirty-info (if dirty " ✎" " ✔")))
        (concat (propertize "⎇ " 'face 'modus-themes-fg-green-warmer)
                (propertize branch 'face 'modus-themes-fg-magenta-warmer)
                (propertize dirty-info 'face
                            (if dirty 'modus-themes-fg-red 'modus-themes-fg-green))))))

  (defun eshell-prompt-multiline ()
    "Eshell Multiline Git prompt."
    (let ((separator (propertize " | " 'face 'font-lock-comment-face))
          (hr (propertize (concat "\n" (make-string (/ (window-total-width) 2) ?─) "\n") 'face 'font-lock-comment-face))
          (dir (propertize (format "%s" (abbreviate-file-name (eshell/pwd))) 'face 'modus-themes-fg-yellow-warmer))
          (git-info (eshell-git-info))
          (time (propertize (format-time-string "%H:%M:%S") 'face 'font-lock-comment-face))
          (sign (if (= (user-uid) 0)
                    (propertize "\n#" 'face 'modus-themes-fg-blue-intense)
                  (propertize "\nλ" 'face 'modus-themes-fg-red-warmer))))
      (concat hr dir separator git-info separator time sign " ")))

  (setf eshell-prompt-function 'eshell-prompt-multiline
        eshell-highlight-prompt nil)
  ;; Aliases
  (defun eshell/o (file)
    "Open FILE."
    (find-file file))

  (defvar thanos/aliases
    '((ll . "ls -lah")
      (clear . clear-scrollback)))

  (defun thanos/set-eshell-aliases (aliases)
    "Set ALIASES as eshell aliases."
    ;; Remove aliases file
    (when (and eshell-aliases-file
               (file-exists-p eshell-aliases-file))
      (delete-file eshell-aliases-file))
    (mapc (lambda (alias)
            (let ((name (symbol-name (car alias)))
                  (command (cdr alias)))
              (eshell/alias name
                            (cond
                             ((stringp command) command)
                             ((symbolp command) (symbol-name command))
                             (t (error "Unsupported alias command type"))))))
          aliases))

  ;; Rebinds
  (defun thanos/eshell-clear ()
    "Interactive call for clear-scrollback."
    (interactive)
    (eshell/clear-scrollback))

  (defun thanos/eshell-preview-insert ()
    (interactive)
    (completion-preview-insert)
    ;; This funciton just deletes the extra space inserted after
    ;; completion.
    (delete-char -1))
  :bind (("C-c e" . eshell)
         :map eshell-mode-map
         ("C-l" . 'thanos/eshell-clear)
         ("<tab>" . 'thanos/eshell-preview-insert)
         ("C-M-i" . 'completion-at-point))
  :hook ((eshell-mode . (lambda ()
                          (thanos/set-eshell-aliases thanos/aliases)
                          (display-line-numbers-mode -1)
                          (eshell-cmpl-mode -1)))))

Eat

Emulate a terminal (Eat)

  • xterm-like emulator, written in Emacs Lisp.
  • Although it’s a standalone terminal for Emacs, similar to term, I’ve mostly used this package to enhance eshell.

My configuration

(use-package eat
  :ensure t
  :config
  (setf eshell-visual-commands nil
        eat-term-name "xterm-256color")
  :bind (("C-c V" . eat))
  :hook ((eshell-mode . eat-eshell-mode)
         (eshell-mode . eat-eshell-visual-command-mode)
         (eat-mode . (lambda () (visual-line-mode -1)))))

Term & Ansi-Term

  • M-x term | M-x ansi-term
    • ansi-term is the same as term, except that it always creates a new buffer and C-x is being marked as an escape-char.
  • Runs a subshell with input and output through an Emacs buffer.

How to use

  • While in term-mode C-c CHAR is equivalent to C-x CHAR.
    • Example C-c o is the global binding C-x o other-window
  • C-c C-j switches to term-line-mode which allows navigation similar to shell
  • C-c C-k switches back to term-char-mode