/dot.emacs.d_minimal

Emacs Minimal Configuration using Org Mode

Primary LanguageEmacs Lisp

Emacs Minimal Configuration

This document assumes a basic understanding of GNU Emacs and Org Mode - Summary and uses literate programming for generation of configuration files, for more information see Org Mode - Extracting Source Files.

This is targeted to minimal installations, it assumes a recent emacs (tested on v26.3) and it require no package installation nor external dependencies.

Use M-x org-babel-tangle to generate init.el.

Installation

git clone https://github.com/ctarbide/dot.emacs.d_minimal ~/.emacs.d

Preamble

;; -*- mode:emacs-lisp; coding:utf-8-unix; lexical-binding:t -*-

;; WARNING: This is generated automatically from README.txt using
;; Org-Mode. All changes here will be lost, eventually.

;; Lastest revision at https://github.com/ctarbide/dot.emacs.d_minimal/blob/master/README.txt

(let* ((thisdir (file-name-directory (or load-file-name buffer-file-name)))
       (custom-settings (expand-file-name "custom-settings.el" thisdir)))
  (when (file-exists-p custom-settings)
    (load-file custom-settings)))

(when (getenv "INSIDE_EMACS")
  (error "Running emacs inside emacs? Are you sure about that?")
  (kill-emacs 1))

(defun clear-kill-ring-and-gc ()
  (interactive)
  (setq kill-ring nil) (garbage-collect))

(defmacro z--defun-name-aware (name arglist &optional docstring &rest body)
  (declare (doc-string 3) (indent 2))
  (if (or (stringp docstring)
          (eq 'interactive (car-safe docstring))
          (eq 'declare (car-safe docstring)))
      `(defun ,name ,arglist ,docstring (let ((defun-name ',name)) ,@body))
    ;; assume docstring is part of the body
    `(defun ,name ,arglist (let ((defun-name ',name)) ,docstring ,@body))))

Some Improvements

(when (<= gc-cons-threshold 800000)
  (setq gc-cons-threshold 10000000))
(when (<= large-file-warning-threshold 10000000)
  (setq large-file-warning-threshold 100000000))

Cd to a more convenient place

On MS Windows emacs_dir is a special variable, see Misc Variables for more information.

(when (string-prefix-p (expand-file-name "bin" (getenv "emacs_dir")) default-directory t)
  (setq default-directory (expand-file-name "~")))

Remove distractions

Taken from whattheemacsd.com and www.sandeepnambiar.com.

;; enable (menu is didactic)
(if (fboundp 'menu-bar-mode) (menu-bar-mode))

;; disable
(if (fboundp 'tool-bar-mode) (tool-bar-mode 0))
(if (fboundp 'scroll-bar-mode) (scroll-bar-mode 0))

Prefer a sane encoding on all platforms

(prefer-coding-system 'utf-8-unix)

Tabs or spaces? Spaces!

(setq-default indent-tabs-mode nil)

Original value of 70 is a bit too narrow

(set-fill-column 90)

Set some important environment variables

https://stackoverflow.com/questions/2183900/how-do-i-prevent-git-diff-from-using-a-pager/2183920

(setenv "PAGER" "cat")
(setenv "GIT_PAGER" "cat")

;; configure git color with:
;;  git config --global color.ui always
;;  git config --global color.log always
;;  git config --global color.diff always
;;  git config --global color.status always
;;  git config --global color.branch always
;;  git config -l | grep color

Customization

(custom-set-variables
 ;; custom-set-variables was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 '(blink-cursor-blinks 0)
 '(confirm-kill-emacs 'y-or-n-p)
 '(inhibit-startup-screen t)
 '(show-paren-delay 0.0)
 '(show-paren-mode t)
 '(show-paren-when-point-inside-paren t)
 ;; '(org-ellipsis " ● ● ●")
 ;; '(org-ellipsis " ○ ○ ○")
 '(org-ellipsis " ◦◦◦")
 '(org-hide-leading-stars t)
 '(org-babel-load-languages
   '((emacs-lisp . t)
     (lisp . t)
     (perl . t)
     (shell . t))))

Load ‘wombat theme

It is such a nice theme.

(when (and (display-graphic-p) (equal custom-known-themes '(user changed)))
  (load-theme 'wombat t t)
  (add-hook 'after-init-hook '(lambda () (enable-theme (car custom-known-themes))))
  (when (and (memq system-type '(ms-dos windows-nt)) (> emacs-major-version 24))
    (add-hook 'window-setup-hook '(lambda () (enable-theme (car custom-known-themes))))))

It is possible to customize a theme using these commands in custom-settings.el

(load-theme 'tango-dark t t)
(add-hook 'after-init-hook '(lambda () (enable-theme (car custom-known-themes))))

See also:

  • (describe-variable ‘custom-enabled-themes)
  • (describe-function ‘disable-theme)

Highlight Selected Window

(defun highlight-selected-window ()
  "Highlight selected window with a different background color."
  (walk-windows (lambda (w)
                  (unless (eq w (selected-window))
                    (with-current-buffer (window-buffer w)
                      (buffer-face-set '(:background "#111" :foreground "#444"))))))
  (buffer-face-set 'default))

(add-hook 'window-state-change-hook 'highlight-selected-window)  ;; ideal hook

Testing

(view-echo-area-messages)
(describe-function 'stringp)
(describe-variable 'indent-tabs-mode)

IDO

IDO will save you a lot of time in finding files and buffers, use C-f (after C-x C-f) and C-b (after C-x b) to fall back to standard minibuffer. More information in Mastering Emacs Book.

See also:

(require 'ido)
(setq ido-enable-flex-matching t)
(setq ido-auto-merge-work-directories-length -1)
(ido-everywhere)
(ido-mode)

Show Trailing Whitespaces

Found in M. Briggs dot files.

(add-hook 'after-change-major-mode-hook
          (lambda ()
            (when (and
                   (not (eq major-mode 'Custom-mode))
                   (not (eq major-mode 'shell-mode))
                   (not (eq major-mode 'emacs-pager-mode))
                   (not (eq major-mode 'term-mode))
                   (not (eq major-mode 'ibuffer-mode))
                   (not (eq major-mode 'rspec-compilation-mode))
                   (not (eq major-mode 'Buffer-menu-mode))
                   (not (eq major-mode 'prodigy-mode))
                   (not (eq major-mode 'Info-mode)))
              (setq show-trailing-whitespace t))))

Nice Utilities and Key Bindings

(defun dos2unix ()
  "CR-LF to LF"
  (set-buffer-file-coding-system 'utf-8-unix 't))

(defun associated-buffer-directory-name ()
  "get a file name associated with the buffer"
  (let ((bfn (buffer-file-name))
        (lbd (bound-and-true-p list-buffers-directory)))
    (or (and bfn (abbreviate-file-name (file-name-directory bfn)))
        (and lbd (abbreviate-file-name lbd)))))

(defun rtrim-and-remove-last-slash (path)
  (replace-regexp-in-string "[\\/[:space:]]+$" "" path nil nil nil))

(defun friendly-path (path)
  (rtrim-and-remove-last-slash (abbreviate-file-name (expand-file-name path))))

(defun unique-buffer-name-from-path (path)
  (let ((p (friendly-path path)))
    (concat (substring (secure-hash 'sha1 p) 0 4) " " (file-name-nondirectory p))))

(defun pop-to-ansi-term-line-mode (buffer-name &rest args)
  "pop-to-buffer-same-window and ansi-term, use C-c C-k to use char-mode, C-x C-j to bring back line-mode"
  (pop-to-buffer-same-window
   (with-current-buffer
       (apply 'term-ansi-make-term
              (or buffer-name (concat "*" (unique-buffer-name-from-path (car args)) "*"))
              (car args) nil (cdr args))
     (current-buffer))))

(defun pop-to-ansi-term-char-mode (buffer-name &rest args)
  "pop-to-buffer-same-window and ansi-term, use C-x C-j to use line-mode, C-c C-k to bring back char-mode"
  (pop-to-buffer-same-window
   (with-current-buffer
       (apply 'term-ansi-make-term
              (or buffer-name (concat "*" (unique-buffer-name-from-path (car args)) "*"))
              (car args) nil (cdr args))
     (term-char-mode)
     (current-buffer))))

(defun kill-ring-clear ()
  (setq kill-ring nil)
  (garbage-collect))

Listing of pop-to-ansi-term-* usage examples.

Useful shortcuts:

C-x C-j
switch ansi-term to line-mode
C-c C-k
switch ansi-term to char-mode

Usage examples:

(require 'term)

(pop-to-ansi-term-char-mode (generate-new-buffer-name "*top*") "top")

(with-current-buffer (pop-to-ansi-term-char-mode nil "top")
  (display-line-numbers-mode 0))

(pop-to-ansi-term-char-mode nil "/usr/bin/top")

;; C-c C-c to send SIGINT
(pop-to-ansi-term-char-mode nil "watch" "-n5" "-d" "ls" "-lh")

(pop-to-ansi-term-line-mode
 (generate-new-buffer-name "*sh*")
 "/bin/sh" "-c" "echo running $0; for i in \"$@\"; do echo \"[$i]\"; done" "inline-script" "a" "b c" " d ")

Eshell is nice and got me some workflow improvements, but grueling bugs and anoyances appear between emacs releases, and stuff that worked previously, stops working, using shell (thank you so much Olin Shivers and Simon Marshall) and a real shell (zsh below) is a more future proof and sane approach.

;; "-ic" flag enable zsh aliases in shell commands, https://github.com/syl20bnr/spacemacs/issues/13401
(when (string= "zsh" (file-name-nondirectory shell-file-name))
  (setq shell-command-switch "-ic"))

(require 'shell) ;; define shell and comint variables

;; TODO: error if 'where does not exist
(defun create-custom-shell (program shell-args where echoes force-new)
  "versatile custom shell creation"
  (let* ((where (expand-file-name where))
         (default-directory where)
         (path (executable-find program))
         (bname (format "*shell* %s" (unique-buffer-name-from-path where)))
         (comint-terminfo-terminal "linux")
         (explicit-shell-file-name path)
         (shell-file-name path))
    (set (intern (format "explicit-%s-args" (file-name-nondirectory path))) shell-args)
    (with-current-buffer (shell (if force-new (generate-new-buffer bname) (get-buffer-create bname)))
      ;; local variables must be set after setting buffer mode, due to
      ;; a kill-all-local-variables being issued when entering a major
      ;; mode, look at comint.el for the list of buffer local
      ;; variables
      (setq-local comint-buffer-maximum-size 100000)
      (add-hook 'comint-output-filter-functions 'comint-truncate-buffer t t)
      (setq-local comint-process-echoes echoes))))

;; zsh -V is for NO_PROMPT_CR (unsetopt prompt_cr)
(defun create-zsh-shell (where &optional force-new)
  (interactive "DWhere? \nP")
  (let* ((default-directory where))
    (create-custom-shell "zsh" '("-iV") where t force-new)))

(defalias 'zsh #'create-zsh-shell)

(defun create-bash-shell (where &optional force-new)
  (interactive "DWhere? \nP")
  (let* ((default-directory where))
    (create-custom-shell "bash" '("-i") where nil force-new)))

(defalias 'bash #'create-bash-shell)

(defun buffer-list-shell-mode ()
  (seq-filter (lambda (b) (eq 'shell-mode (buffer-local-value 'major-mode b))) (buffer-list)))

(defun buffer-list-files ()
  (seq-filter #'buffer-file-name (buffer-list)))

(defun z--buffer-is-writable-p (buf)
  (not (buffer-local-value 'buffer-read-only buf)))

(defun sort-predicate-has-process (a b)
  (and (get-buffer-process a) (not (get-buffer-process b))))

(defun sort-predicate-writable (a b)
  (and (z--buffer-is-writable-p a) (not (z--buffer-is-writable-p b))))

(defun sort-predicate-modified (a b)
  (and (buffer-modified-p a) (not (buffer-modified-p b))))

(defun buffer-list-shell-mode-running-first (&optional buffer-list)
  (sort (or buffer-list (buffer-list-shell-mode)) #'sort-predicate-has-process))

(defun buffer-list-files-writable-first (&optional buffer-list)
  (sort (or buffer-list (buffer-list-files)) #'sort-predicate-writable))

(defun buffer-list-files-modified-first (&optional buffer-list)
  (sort (or buffer-list (buffer-list-files)) #'sort-predicate-modified))

(defun buffer-list-files-modified-writable-first (&optional buffer-list)
  (sort (or buffer-list (buffer-list-files))
        (lambda (a b)
          (let ((m (sort-predicate-modified a b))
                (w (sort-predicate-writable a b)))
            (or (and m w) m w)))))

(defun list-shells (&optional arg)
  (interactive "P")
  (let* ((buffers (if arg (buffer-list-files-modified-writable-first)  (buffer-list-shell-mode-running-first)))
         (buffer-list (list-buffers-noselect nil buffers))
         (column-title (if arg "Buffer (files only, writable first)" "Buffer (shells only, running first)")))
    (when buffers
      (with-current-buffer buffer-list
        (aset tabulated-list-format 3 `(,column-title 35 t))
        (tabulated-list-init-header)
        (tabulated-list-print t)))
    (pop-to-buffer-same-window buffer-list)))

(z--defun-name-aware z--create-shell (where &optional force-new)
  (interactive "DWhere? \nP")
  (defalias defun-name
    (if (equal 0 (call-process "which" nil nil nil "zsh"))
        #'create-zsh-shell
      #'create-bash-shell))
  (funcall (symbol-function defun-name) where force-new))

(global-set-key (kbd "C-x x") 'z--create-shell)

;; use 'T' twice (or 'g' once) in buffer list to list all buffers
(global-set-key (kbd "C-x C-b") #'list-shells)

Other (Sandeep Nambiar)

line numbers

(global-hl-line-mode 0)
(line-number-mode)
(global-display-line-numbers-mode 0)
(global-linum-mode 0)
(column-number-mode)
(size-indication-mode)
(blink-cursor-mode)

current file’s name to model-line

(setq frame-title-format
      '((:eval (if (buffer-file-name)
                   (abbreviate-file-name (buffer-file-name))
                 "%b"))))

y-or-n for everything

(defalias 'yes-or-no-p 'y-or-n-p)

All Done!

Just a simple debuging message.

(custom-set-faces
 ;; custom-set-faces was added by Custom.
 ;; If you edit it by hand, you could mess it up, so be careful.
 ;; Your init file should contain only one such instance.
 ;; If there is more than one, they won't work right.
 )

(message "All done with %s!" (file-name-nondirectory (or load-file-name buffer-file-name)))

Automated Extraction

See Batch Execution.