Skip to content

pascal-huber/emacs.d

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Emacs Configuration

This is my Emacs configuration.

Content

Notes

Some functions I should use more often:

  • Use evil-window commands (e.g. C-w j)
  • Use C-c u more often (counsel-imenu).
  • Use C-c p 5 f to open a file in a new frame with projectile.
  • Use `` to jump back and forth.
  • Use m <letter> to create markers ` <letter> to go to marker.
  • Use C-i (jump-forward) and C-o (jump-backward).
  • The font-size can be adjusted buffer-locally with C-x C-+ and C-x C--.

Packages to check out:

  • straight.el - Next-generation, purely functional package manager for the Emacs hacker.
  • diff-hl or emacs-git-gutter - Highlighting uncommitted changes.
  • company-tabnine - A company-mode backend for TabNine, the all-language autocompleter.
  • magit forge - Work with Git forges from the comfort of Magit.
  • aggressive-indent-mode - Emacs minor mode that keeps your code always indented. More reliable than electric-indent-mode.
  • use-package - A use-package declaration for simplifying your .emacs.

Other things to check out and problems to deal with:

  • Use project-specific exec-path. For example to use eslint from the project’s ./node_modules/ and not from globally installed modules.
  • Check out this company-yasnippet setup

Inspiration

Installation

$ cd ~/git
$ git clone http://github.com/sirpscl/emacs.d
$ ln -s ~/git/emacs.d ~/.emacs.d

Some important and noteworthy dependencies:

After the first start, run the following commands.

(jedi:install-server)

Packages

Sources

Use the gnu (official) and melpa (unofficial) archives.

(require 'package)
(setq package-archives
      '(("gnu"   . "https://elpa.gnu.org/packages/")
        ("melpa" . "https://melpa.org/packages/")))
(package-initialize)

Installation

Install packages on demand. This is cleaner and easier to keep tidy than defining a huge list of packages at the beginning of the configuration.

(defun ph/install-package (package)
  "Install PACKAGE if not yet installed."
  (unless (package-installed-p package)
    (progn
      (package-refresh-contents)
      (package-install package))))

Updates

Update all packages with M-x auto-package-update-now.

(ph/install-package 'auto-package-update)
(require 'auto-package-update)
(setq auto-package-update-interval 7)

To calculate the number of available updates, package-refresh-contents needs to be executed. Doing this periodically is annoying since it will block emacs. I therefore use the following function to query the number of auto-package-update-interval since the last update for my status bar so that I remember to update my packages regularly.

(defun ph/update-intervals-since-last-update ()
  "Return the number of auto-update-package-intervals since the
last update."
  (when (file-exists-p auto-package-update-last-update-day-path)
    (/ (- (apu--today-day) (apu--read-last-update-day))
       auto-package-update-interval)))

Configuration Helpers

General provides a more convenient method for binding keys in emacs (for both evil and non-evil users).

(ph/install-package 'general)

Other Helpers:

(defun ph/call-rotate (fn lst)
  "Call FN with first element of the LST.
Returns the rotated list."
  (let ((args (car lst)))
    (funcall fn args)
    (append (cdr lst) (cons args ()))))

Tweaking Defaults

This section contains customizations of Emacs’ default settings and built-in packages configuration and extensions.

Disabled Commands

Enable all disabled commands.

(setq disabled-command-function nil)

Yes or No

For reasons of simplicity.

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

Symlinks

Always Follow Symlinks, no questions asked.

(setq vc-follow-symlinks t)

Display Line Numbers

Show line numbers in all text and programming buffers.

(add-hook 'text-mode-hook 'display-line-numbers-mode)
(add-hook 'prog-mode-hook 'display-line-numbers-mode)

Count the number of lines to use for line number width.

(setq display-line-numbers-width-start t)

Final Newline

Automatically add a newline at the end of a file.

(setq require-final-newline t)

Backup

No backups, commit frequently!

(setq make-backup-files nil)

Auto-Save

Store auto-saves in /tmp

(setq auto-save-file-name-transforms
      `((".*" ,temporary-file-directory t)))

Keystrokes

Show my keystrokes almost immediately in the echo-area.

(setq echo-keystrokes 0.1)

Scroll

When scrolling, keep the cursor at the same position.

(setq scroll-preserve-screen-position 'keep)

Auto revert

When something changes a file, automatically refresh the buffer containing that file so they can’t get out of sync.

(global-auto-revert-mode t)

Update vc-info when reverting (e.g. after changing branch). Note that this may cause performance issues when many buffers are present.

(setq auto-revert-check-vc-info t)

Garbage Collection

Collect garbage after 20MB. Some packages which cache a lot (e.g. flx-ido) will profit.

(setq gc-cons-threshold (* 20 1000 1000))

Browser

Use Firefox to browse URLs.

(setq browse-url-browser-function 'browse-url-generic
      browse-url-generic-program "firefox"
      browse-url-generic-args '("--private-window")
      browse-url-new-window-flag t)

Line Filling

Use a line width of 80 columns.

(setq-default fill-column 80)

To reorganize a paragraph to fit the 80 columns, use M-q (M-x fill-paragraph) and/or enable auto-fill-mode.

Don’t do double-spaces between sentences.

(setq-default sentence-end-double-space nil)

To undo paragraph and region reorganization. Stolen from here.

(defun ph/unfill-paragraph (&optional region)
  "Takes a multi-line paragraph and makes it into a single line
of text."
  (interactive (progn (barf-if-buffer-read-only) '(t)))
  (let ((fill-column (point-max))
        ;; This would override `fill-column' if it's an integer.
        (emacs-lisp-docstring-fill-column t))
    (fill-paragraph nil region)))

Whitespace Cleanup

Delete trailing whitespaces when saving.

(add-hook 'write-file-hooks 'delete-trailing-whitespace)

Buffers and Frames

Split functions which open the previous buffer in the new window instead of showing the current buffer twice. Stolen shamelessly from here

(defun ph/vsplit-last-buffer ()
  (interactive)
  (split-window-vertically)
  (other-window 1 nil)
  (switch-to-next-buffer))

(defun ph/hsplit-last-buffer ()
  (interactive)
  (split-window-horizontally)
  (other-window 1 nil)
  (switch-to-next-buffer))

A function to open the previous buffer in a new frame.

(defun ph/open-last-buffer ()
  (interactive)
  (switch-to-buffer-other-frame (other-buffer)))

Ido

Ido (“interactively do things”) supercharges Emacs’ completion system. I use ido everywhere ivy is not set up.

(ido-mode 1)
(ido-everywhere 1)

Enable the built-in fuzzy-matching

(setq ido-enable-flex-matching t)

ido-vertical-mode makes ido-mode display vertically.

(ph/install-package 'ido-vertical-mode)
(ido-vertical-mode 1)
(setq ido-vertical-define-keys 'C-n-and-C-p-only)
(setq ido-vertical-show-count t)

Sudo Save

If the current buffer is not writable, ask if it should be saved with sudo.

(defun ph/sudo-file-name (filename)
  "Prepend '/sudo:root@`system-name`:' to FILENAME if appropriate.
If the file already has a tramp prefix, return nil."
  (when (and filename
             (not (file-remote-p filename)))
    (format "/sudo:root@%s:%s" (system-name) filename)))

(defun ph/sudo-save-buffer ()
  "Save buffer as root if the user approves."
  (let ((filename (ph/sudo-file-name (buffer-file-name))))
    (when (and filename
               (yes-or-no-p (format "Save file as %s ? " filename)))
      (write-file filename))))

(advice-add 'save-buffer :around
            '(lambda (fn &rest args)
               (when (or (not (buffer-file-name))
                         (not (buffer-modified-p))
                         (file-writable-p (buffer-file-name))
                         (not (ph/sudo-save-buffer)))
                 (call-interactively fn args))))

Flyspell

Flyspell enables on-the-fly spell checking in Emacs by the means of a minor mode. Flyspell highlights incorrect words as soon as they are completed or as soon as the TextCursor hits a new word.

Flyspell-Correct offers distraction-free words correction with flyspell via selected interface.

(ph/install-package 'flyspell-correct-ivy)
(setq flyspell-correct-interface #'flyspell-correct-ivy)

Use Hunspell instead of Aspell.

(setq ispell-program-name "hunspell")

Rotate through Ispell languages

(setq ph/ispell-dictionaries-list '("en_US" "de_CH"))

(defun ph/ispell-next-dictionary ()
  "Load next Ispell dictionary."
  (interactive)
  (setq ph/ispell-dictionaries-list
        (ph/call-rotate 'ispell-change-dictionary
                         ph/ispell-dictionaries-list)))

(add-hook 'after-init-hook 'ph/ispell-next-dictionary)

Occur

Occur-Mode is a search minor-mode that shows a buffer with all matching results in a popup buffer. Use the occur-dwim (do what I mean) function from (or emacs irrelevant)

(defun ph/occur-dwim ()
  "Call `occur' with a sane default."
  (interactive)
  (push (if (region-active-p)
            (buffer-substring-no-properties
             (region-beginning)
             (region-end))
          (let ((sym (thing-at-point 'symbol)))
            (when (stringp sym)
              (regexp-quote sym))))
        regexp-history)
  (call-interactively 'occur))

Dired

Usage:

  • a to open a file or directory in the current buffer
  • RET to open a file or directory in a new buffer
  • o to open a file or directory in a vertical split buffer
  • C-o to open a file or directory in a vertical split buffer but keep the focus in the current buffer.
  • C-c C-o to open a file or directory in a new frame.

Reuse buffer

(put 'dired-find-alternate-file 'disabled nil)

Show all files, in long listing format and human readable units.

(setq-default dired-listing-switches "-lh")

Open in new frame

(defun ph/dired-find-file-other-frame ()
  "In Dired, visit this file or directory in another window."
  (interactive)
  (find-file-other-frame (dired-get-file-for-visit)))

(eval-after-load "dired"
  '(define-key dired-mode-map (kbd "C-c C-o") 'ph/dired-find-file-other-frame))

Org-Mode

Org-Mode is for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system.

(require 'org)

Settings

Automatically fill lines

(add-hook 'org-mode-hook 'auto-fill-mode)

Don’t ask every time when executing a code block.

(setq org-confirm-babel-evaluate nil)

Don’t indent code blocks

(setq org-edit-src-content-indentation 0)

No empty lines between items

(setq org-blank-before-new-entry
      '((heading . nil)
        (plain-list-item . nil)))

TOC-Org

Every time you’ll be saving an org file, the first headline with a :TOC: tag will be updated with the current table of contents.

  • :TOC_2: - sets the max depth of the headlines in the table of contents to 2 (the default)
  • :TOC_2_gh: - sets the max depth as in above and also uses the GitHub-style hrefs in the table of contents (this style is default). The other supported href style is ‘org’, which is the default org style.
(ph/install-package 'toc-org)
(add-hook 'org-mode-hook 'toc-org-enable)

Kill Buffers

Kill all but the current buffer. Stolen shamelessly from here.

(defun ph/kill-other-buffers ()
  "Kill all other buffers."
  (interactive)
  (mapc 'kill-buffer (delq (current-buffer) (buffer-list))))

Additional Functionality

This section contains some third party packages and additional functionality.

Ace Window

Ace-window aims to take the speed and predictability of windmove and pack it into a single key binding, similar to other-window.

(ph/install-package 'ace-window)

Use the following characters/keys to switch to the windows

(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))

Set the ace-window-scope to frame because I often have multiple frames opened on other workspaces and only want to jump to other windows in the current frame.

(setq aw-scope 'frame)

Smex

Smex is a M-x enhancement for Emacs. Built on top of Ido, it provides a convenient interface to your recently and most frequently used commands. And to all the other commands, too.

(ph/install-package 'smex)

Browse-Kill-Ring

(ph/install-package 'browse-kill-ring)

(setq browse-kill-ring-highlight-inserted-item t
      browse-kill-ring-highlight-current-entry nil
      browse-kill-ring-show-preview t)

(general-def browse-kill-ring-mode-map
  "k" 'browse-kill-ring-previous
  "j" 'browse-kill-ring-forward)

Crux

A Collection of Ridiculously Useful eXtensions for Emacs. crux bundles a few useful interactive commands to enhance your overall Emacs experience.

(ph/install-package 'crux)

Docker

(ph/install-package 'dockerfile-mode)

Ag

Ag allows you to search using ag from inside Emacs. You can filter by file type, edit results inline, or find files.

(ph/install-package 'ag)

Ripgrep

Ripgrep (rg) is a replacement for both grep like (search one file) and ag like (search many files) tools. It’s fast and versatile and written in Rust.

(ph/install-package 'rg)

Evil

Evil is an extensible vi layer for Emacs. It emulates the main features of Vim, and provides facilities for writing custom extensions.

(ph/install-package 'evil)
(require 'evil)
(evil-mode 1)

States

Set initial state by major mode.

(dolist (mode-map '((ag-mode . emacs)
                    (calendar-mode . emacs)
                    (elfeed-show-mode . emacs)
                    (elfeed-search-mode . emacs)
                    (eshell-mode . emacs)
                    (flycheck-error-list-mode . emacs)
                    (git-commit-mode . insert)
                    (git-rebase-mode . emacs)
                    (haskell-error-mode . emacs)
                    (haskell-interactive-mode . emacs)
                    (help-mode . emacs)
                    (inferior-ess-mode . emacs)
                    (inf-ruby-mode . emacs)
                    (intero-repl-mode . emacs)
                    (pdf-occur-buffer-mode . emacs)
                    (rspec-compilation-mode . emacs)
                    (shell-mode . emacs)
                    (term-mode . emacs)))
  (evil-set-initial-state `,(car mode-map) `,(cdr mode-map)))

Evil Surround

  • Add surrounding ​'​ with S'​ from visual-state (use viw to mark current word)
  • Change surrounding ​'​ to * with cs'*
  • Remove surrounding * with ds*
(ph/install-package 'evil-surround)
(global-evil-surround-mode 1)

Free Keys

Free M-. and M-,​ since they are popular keybindings for “jump to definition” and “back”. Also I don’t use evil-complete.

(general-def 'normal
  "M-." nil
  "M-," nil)

(general-def 'insert
  "C-p" nil
  "C-n" nil)

God Mode

Evil Goddess makes it possible for evil users to conveniently access Emacs keybindings without touching the control and meta key - similar to god-mode.

In order not to loose (hard-coded) which-key support, it is called god-mode and thus conflicts with the original god-mode.

See evil-goddess/README.md for more details.

(add-to-list 'load-path "~/.emacs.d/evil-goddess/")
(require 'god-mode)

Which-Key

Which-Key is a minor mode for Emacs that displays the key bindings following your currently entered incomplete command (a prefix) in a popup.

(ph/install-package 'which-key)
(which-key-mode)

;; (use-package which-key
;;   :bind (("C-' k" . which-key-mode)
;;          ("C-' C-k" . which-key-mode))
;;   :init
;;   (which-key-setup-side-window-bottom)
;;   (which-key-enable-god-mode-support)
;;   ;; ...
;;   (which-key-mode t))

Also use which-key evil and god-mode shortcuts.

(setq which-key-allow-evil-operators t)
(setq which-key-show-operator-state-maps t)

Show command names up to 40 columns before cutting them.

(setq which-key-max-description-length 25)

Show which-key after .4 seconds inactivity

(setq which-key-idle-delay 0.4)
(which-key-setup-side-window-bottom)
  (which-key-enable-god-mode-support)
(which-key-enable-god-mode-support)

Projectile

Projectile is a project interaction library for Emacs. Its goal is to provide a nice set of features operating on a project level without introducing external dependencies (when feasible).

(ph/install-package 'projectile)
(projectile-mode +1)
(general-def projectile-mode-map
  "C-c p" 'projectile-command-map)
(setq projectile-completion-system 'ivy)

Ivy, Counsel and Swiper

Ivy, Counsel (and Swiper), a collection of Ivy-enhanced versions of common Emacs commands.

  • Ivy is a generic completion mechanism for Emacs.
  • Counsel is a collection of Ivy-enhanced versions of common Emacs commands.
  • Swiper is an Ivy-enhanced alternative to isearch.

Installing counsel will also install the other two as dependencies.

(ph/install-package 'counsel)
(require 'swiper)

Use ivy for completion instead of ido.

(ivy-mode 1)
(setq ivy-use-virtual-buffers t)
(setq enable-recursive-minibuffers t)

Some packages need special attention.

(setq magit-completing-read-function 'ivy-completing-read)
(setq projectile-completion-system 'ivy)
(setq mu4e-completing-read-function 'ivy-completing-read)

Show current entry number.

(setq ivy-count-format " %d/%d ")

Prevent swiper from swiping itself.

(defun ph/swiper-from-isearch ()
  (interactive)
  (unless (string= (symbol-name major-mode) "minibuffer-inactive-mode")
    (swiper-from-isearch)))

PDF-Tools

Pdf-Tools is, among other things, a replacement of DocView for PDF files. The key difference is that pages are not pre-rendered by e.g. ghostscript and stored in the file-system, but rather created on-demand and stored in memory.

(ph/install-package 'pdf-tools)
(require 'pdf-tools)
(pdf-tools-install-noverify)

When highlighting, automatically add an annotation.

(setq pdf-annot-activate-created-annotations t)

Zoom by 10%.

(setq pdf-view-resize-factor 1.1)

Keybindings:

  • C-c C-a h to highlight text
  • C-c C-a o to strike though text
  • C-c C-a t to add a note
  • C-c C-a D to delete one of the above
  • C-c C-a l to list all annotations. Use SPACE to jump to the annotation.
  • and more

XDG-open

Open File in External App. Stolen shamelessly from here.

(defun ph/xdg-open (&optional @fname)
  "Open the current file or dired marked files in external app.
The app is chosen from your OS's preference.

When called in emacs lisp, if @fname is given, open that.

URL `http://ergoemacs.org/emacs/emacs_dired_open_file_in_ext_apps.html'
Version 2019-01-18"
  (interactive)
  (let* (
         ($file-list
          (if @fname
              (progn (list @fname))
            (if (string-equal major-mode "dired-mode")
                (dired-get-marked-files)
              (list (buffer-file-name)))))
         ($do-it-p (if (<= (length $file-list) 5)
                       t
                     (y-or-n-p "Open more than 5 files? "))))
    (when $do-it-p
      (cond
       ((string-equal system-type "windows-nt")
        (mapc
         (lambda ($fpath)
           (w32-shell-execute "open" (replace-regexp-in-string "/" "\\" $fpath t t))) $file-list))
       ((string-equal system-type "darwin")
        (mapc
         (lambda ($fpath)
           (shell-command
            (concat "open " (shell-quote-argument $fpath))))  $file-list))
       ((string-equal system-type "gnu/linux")
        (mapc
         (lambda ($fpath) (let ((process-connection-type nil))
                            (start-process "" nil "xdg-open" $fpath))) $file-list))))))

Mu4e

Mu4e is an emacs-based e-mail client. It’s based on the mu e-mail indexer/searcher. It attempts to be a super-efficient tool to withstand the daily e-mail tsunami.

(require 'mu4e)
(require 'mu4e-contrib)
(ph/install-package 'smtpmail)

General

Mail directory

(setq mu4e-maildir "~/.mail")

Save attachments in ~/Downloads/.

(setq mu4e-attachment-dir  "~/Downloads")

Close mu4e without asking.

(setq mu4e-confirm-quit nil)

Open mu4e in the current frame or switch to an already existing mu4e-buffer.

(defun ph/mu4e ()
  "Open or switch to mu4e."
  (interactive)
  (unless (string-prefix-p "mu4e" (symbol-name major-mode))
    (let ((buffer (get-buffer "*mu4e-headers*")))
      (if buffer (switch-to-buffer buffer) (mu4e)))))

Hide addresses on main view

(setq mu4e-main-buffer-hide-personal-addresses t)

Hide the annoying indexing message.

(setq mu4e-hide-index-messages t)

Update every 10 minutes

(setq mu4e-get-mail-command "offlineimap")
(setq mu4e-update-interval (* 10 60))

For some reason the first two cited faces are equal by default. Let’s fix this.

(set-face-attribute 'mu4e-cited-2-face nil
                    :foreground "#5fafd7")

Headers

Custom date and time format.

(setq mu4e-headers-time-format "today    %H:%M")
(setq mu4e-headers-date-format "%d.%m.%y %H:%M")

Do not show related messages by default (toggle with W)

(setq mu4e-headers-include-related nil)

Don’t show duplicate messages.

(setq mu4e-headers-skip-duplicates t)

Add default search values for mu4e-headers-search unless arguments are given to mu4e-headers-search or the search is not called from within a mu4e-buffer. The values are set in the context definition (ph/mu4e-default-search-expr).

(advice-add 'mu4e-headers-search :around
            (lambda (fn &rest args)
              (if (and (= 0 (length args))
                       (string-prefix-p "mu4e" (symbol-name major-mode))
                       (< 0 (length ph/mu4e-default-search-expr)))
                  (apply fn (list (concat ph/mu4e-default-search-expr " ")
                                  "Search for: " t))
                (apply fn args))))

Some functions to get some additional information about emails. Stolen shamelessly from here

(defun ph/mu4e-get-user-agent (msg)
  (let ((path (or (mu4e-message-field msg :path) "")))
    (if (or (string= path "")
            (not (file-readable-p path)))
        "no path found"
      (let ((xmailer (ph/mu4e-get-mail-header "x-mailer" path))
            (useragent (ph/mu4e-get-mail-header "user-agent" path)))
        (if (string= xmailer useragent)
            xmailer
          (cond
           ((string= xmailer "") useragent)
           ((string= useragent "") xmailer)
           (t (concat xmailer " (xmailer)\n" useragent " (user-agent)"))))))))

(defun ph/mu4e-get-mail-header (header-name path)
  (replace-regexp-in-string
   "[ \t\n]*$"
   ""
   (shell-command-to-string
    (concat "/usr/bin/sed -n '/^" header-name
            ":/I{:loop t;h;n;/^ /{H;x;s/\\n//;t loop};x;p}' '" path
            "' | sed -n 's/^" header-name
            ": \\(.*\\)$/\\1/Ip'"))))

(add-to-list 'mu4e-header-info-custom
             '(:useragent . (:name "User-Agent"
                                   :shortname "UserAgt."
                                   :help "Mail client used by correspondant"
                                   :function ph/mu4e-get-user-agent)))

Set the fields displayed in mu4e-headers-mode and mu4e-view-mode.

(setq mu4e-headers-fields
      '((:flags        . 4)
        (:human-date   . 15)
        (:from         . 25)
        (:subject)))

Ask before I delete something permanently or set the trash flag. I just move messages to the trash folder to “delete” them.

(defun ph/do-or-dont-execute (fn &rest args)
  "Execute FN (with ARGS) iff I confirm."
  (when (y-or-n-p "Are you sure? ")
    (apply fn args)))

(advice-add 'mu4e-headers-mark-for-delete
            :around 'ph/do-or-dont-execute)
(advice-add 'mu4e-view-mark-for-delete
            :around 'ph/do-or-dont-execute)
(advice-add 'mu4e-headers-mark-for-trash
            :around 'ph/do-or-dont-execute)
(advice-add 'mu4e-view-mark-for-trash
            :around 'ph/do-or-dont-execute)

View

Show the useragent and bcc.

(setq mu4e-view-fields
      '(:from
        :to
        :cc
        :bcc
        :subject
        :flags
        :date
        :maildir
        :mailing-list
        :tags
        :signature
        :decryption
        :useragent
        :attachments))

Show me the addresses, not only names.

(setq mu4e-view-show-addresses t)

View html-mail in browser with aV.

(add-to-list 'mu4e-view-actions
             '("ViewInBrowser" . mu4e-action-view-in-browser) t)

Bookmarks

Custom Bookmarks

(add-to-list 'mu4e-bookmarks
             (make-mu4e-bookmark
              :name  "Big ones"
              :query "size:5M..50000M"
              :key ?b))
(add-to-list 'mu4e-bookmarks
             (make-mu4e-bookmark
              :name  "Bullshit"
              :query "maildir:/.*/.*\\(spam\\|junk\\).*/"
              :key ?s))

Compose

Enabling receiving clients that support this feature to reflow my paragraphs. Plain text emails with Content-Type: text/plain; format=flowed can be reflowed (i.e. line endings removed, paragraphs refilled) by receiving clients that support this standard. Clients that don’t support this, show them as is, which means this feature is truly non-invasive.

(setq mu4e-compose-format-flowed t)

Dont reply to myself.

(setq mu4e-compose-dont-reply-to-self t)

Kill message-buffer when finished.

(setq message-kill-buffer-on-exit t)

Add formatted citation line.

(setq message-citation-line-function
      'message-insert-formatted-citation-line)

Send

Use smtpmail with gnutls to sending mails.

(setq message-send-mail-function 'smtpmail-send-it)
(setq starttls-use-gnutls t)
(setq smtpmail-debug-info t)

Before sending a message, check if it contains any words that indicate that there should be an attachement. If it does, ask if all attachments were added before sending the mail.

(defvar ph/message-attachment-regexp
  (concat "\\("
          "[Ww]e send\\|"
          "[Ii] send\\|"
          "attach\\|"
          "[aA]nhang\\|"
          "[aA]ngehängt\\|"
          "[sS]chicke\\|"
          "haenge\\|"
          "hänge\\)"))

(defun ph/message-check-attachment nil
  "Check for forgotten attachments"
  (save-excursion
    (message-goto-body)
    (when (search-forward-regexp ph/message-attachment-regexp nil t nil)
      (message-goto-body)
      (unless (message-y-or-n-p
               "Did you attach all documents?" nil nil)
        (error "No message sent, add some attachments!")))))

(add-hook 'message-send-hook 'ph/message-check-attachment)

Contexts

Pick first Context as default.

(setq mu4e-context-policy 'pick-first)
(setq mu4e-compose-context-policy 'ask-if-none)

(setq mu4e-contexts
      `(,(make-mu4e-context
          :name "Private"
          :match-func (lambda (msg)
                        (when msg
                          (or
                           (mu4e-message-contact-field-matches
                            msg
                            :to "pascal.huber@resolved.ch")
                           (mu4e-message-contact-field-matches
                            msg
                            :to "accounts@resolved.ch"))))
          :vars '((user-full-name                . "Pascal Huber" )
                  (user-mail-address             . "pascal.huber@resolved.ch")
                  (mu4e-get-mail-command         . "offlineimap")
                  (mu4e-drafts-folder            . "/r/Drafts")
                  (mu4e-sent-folder              . "/r/Sent")
                  (mu4e-trash-folder             . "/r/Trash")
                  (mu4e-maildir-shortcuts
                   .( ("/r/INBOX"                . ?i)
                      ("/r/Sent"                 . ?s)
                      ("/r/Spam"                 . ?x)
                      ("/r/keep"                 . ?k)
                      ("/r/tempKeep"             . ?t)
                      ("/r/Trash"                . ?b)))
                  (mu4e-compose-crypto-reply-plain-policy . sign)
                  (ph/mu4e-default-search-expr   . "maildir:/r/.*[^Trash]/")
                  (mu4e-sent-messages-behavior   . sent)
                  (smtpmail-stream-type          . starttls)
                  (smtpmail-default-smtp-server  . "mail.infomaniak.com")
                  (smtpmail-smtp-server          . "mail.infomaniak.com")
                  (smtpmail-smtp-service         . 587)
                  (smtpmail-smtp-user            . "pascal.huber@resolved.ch")
                  (smtpmail-starttls-credentials . "/home/pascal/.authinfo.gpg")
                  (smtpmail-auth-credentials     . '(("mail.resolved.ch" 587 nil nil)))))
        ,(make-mu4e-context
          :name "QuickShift"
          :match-func (lambda (msg)
                        (when msg
                          (mu4e-message-contact-field-matches
                           msg
                           :to "pascal@quickshift.ch")))
          :vars '((user-full-name                . "Pascal Huber" )
                  (user-mail-address             . "pascal@quickshift.ch")
                  (mu4e-get-mail-command         . "offlineimap")
                  (mu4e-drafts-folder            . "/q/INBOX.Drafts")
                  (mu4e-sent-folder              . "/q/INBOX.Sent")
                  (mu4e-trash-folder             . "/q/INBOX.Trash")
                  (mu4e-maildir-shortcuts
                   .( ("/q/INBOX"                . ?i)
                      ("/q/INBOX.Sent"           . ?s)
                      ("/q/INBOX.spambucket"     . ?x)
                      ("/q/INBOX.keep"           . ?k)
                      ("/q/INBOX.live"           . ?l)
                      ("/q/INBOX.customers"      . ?c)
                      ("/q/INBOX.tempKeep"       . ?k)
                      ("/q/INBOX.bugsnag"        . ?e)
                      ("/q/INBOX.Trash"          . ?b)))
                  (mu4e-compose-crypto-reply-plain-policy . sign)
                  (ph/mu4e-default-search-expr   . "maildir:/q/.*[^Trash]/")
                  (mu4e-sent-messages-behavior   . sent)
                  (smtpmail-stream-type          . starttls)
                  (smtpmail-default-smtp-server  . "mail.quickshift.ch")
                  (smtpmail-smtp-server          . "mail.quickshift.ch")
                  (smtpmail-smtp-service         . 587)
                  (smtpmail-smtp-user            . "pascal@quickshift.ch")
                  (smtpmail-starttls-credentials . "/home/pascal/.authinfo.gpg")
                  (smtpmail-auth-credentials     . '(("mail.quickshift.ch" 587 nil nil)))))
        ,(make-mu4e-context
          :name "ETH"
          :match-func (lambda (msg)
                        (when msg
                          (mu4e-message-contact-field-matches
                           msg
                           :to "pahuber@student.ethz.ch")))
          :vars '((user-full-name                . "Pascal Huber" )
                  (user-mail-address             . "pahuber@student.ethz.ch")
                  (mu4e-get-mail-command         . "offlineimap")
                  (mu4e-drafts-folder            . "/e/Drafts")
                  (mu4e-sent-folder              . "/e/Sent Items")
                  (mu4e-trash-folder             . "/e/Deleted Items")
                  (mu4e-maildir-shortcuts
                   .( ("/e/INBOX"                . ?i)
                      ("/e/Junk E-Mail"          . ?x)
                      ("/e/INBOX.keep"           . ?k)
                      ("/e/INBOX.asl"            . ?a)
                      ("/e/INBOX.iml"            . ?m)
                      ("/e/INBOX.negotiation"    . ?n)
                      ("/e/INBOX.podc"           . ?p)
                      ("/e/Sent Items"           . ?s)
                      ("/e/INBOX.tmp"            . ?t)
                      ("/e/Deleted Items"        . ?b)))
                  (mu4e-compose-crypto-reply-plain-policy . sign)
                  (ph/mu4e-default-search-expr   . "maildir:/e/.*[^Deleted\\ Items]/")
                  (mu4e-sent-messages-behavior   . sent)
                  (smtpmail-stream-type          . starttls)
                  (smtpmail-default-smtp-server  . "mail.ethz.ch")
                  (smtpmail-smtp-server          . "mail.ethz.ch")
                  (smtpmail-smtp-service         . 587)
                  (smtpmail-smtp-user            . "pahuber")
                  (smtpmail-starttls-credentials . "/home/pascal/.authinfo.gpg")
                  (smtpmail-auth-credentials     . '(("mail.ethz.ch" 587 nil nil)))))))

Programming

This section contains programming packages and settings.

General Purpose

Company

Company is a text completion framework for Emacs. The name stands for “complete anything”. It uses pluggable back-ends and front-ends to retrieve and display completion candidates.

(ph/install-package 'company)
(require 'company)
(add-hook 'after-init-hook 'global-company-mode)

Settings

Automatically show completion after 1 character.

(setq company-minimum-prefix-length 1)

Don’t require a match to continue typing.

(setq company-require-match nil)

Switch between suggestions with C-n and C-p.

(general-def company-active-map
  "C-n" 'company-select-next
  "C-p" 'company-select-previous)

(general-def company-search-map
  "C-n" 'company-select-next
  "C-p" 'company-select-previous)

Brackets

Rainbow-Delimiters makes brackets colorful.

(ph/install-package 'rainbow-delimiters)
(add-hook 'prog-mode-hook 'rainbow-delimiters-mode)

Highlight matching brackets.

(setq show-paren-style 'mixed)
(add-hook 'prog-mode-hook 'show-paren-mode)

Dumb Jump

Dumb-Jump is an Emacs “jump to definition” package with support for multiple programming languages that favors “just working”. This means minimal – and ideally zero – configuration with absolutely no stored indexes (TAGS) or persistent background processes

(ph/install-package 'dumb-jump)
(dumb-jump-mode)
(setq dumb-jump-selector 'ivy)
(setq dumb-jump-use-visible-window nil)

Flycheck

Flycheck is a modern on-the-fly syntax checking extension for GNU Emacs. The most important commands have keybindings with prefix C-c !.

(ph/install-package 'flycheck)
(require 'flycheck)

Enable Flycheck globally (prog-mode-hook may not cover all modes).

(add-hook 'after-init-hook 'global-flycheck-mode)
(define-fringe-bitmap 'flycheck-fringe-bitmap-ball
    (vector #b00000000
            #b00000000
            #b00000000
            #b00000000
            #b00000000
            #b00111000
            #b01111100
            #b11111110
            #b11111110
            #b11111110
            #b01111100
            #b00111000
            #b00000000
            #b00000000
            #b00000000
            #b00000000
            #b00000000))

(flycheck-define-error-level 'error
  :severity 100
  :compilation-level 2
  :overlay-category 'flycheck-error-overlay
  :fringe-bitmap 'flycheck-fringe-bitmap-ball
  :fringe-face 'flycheck-fringe-error
  :error-list-face 'flycheck-error-list-error)

Git

Magit is an interface to the version control system Git.

(ph/install-package 'magit)

Magit-Todos shows all TODO items of the projct in the main magit-buffer.

(ph/install-package 'magit-todos)
(magit-todos-mode t)

Some major-modes to configure git repositories.

(ph/install-package 'gitattributes-mode)
(ph/install-package 'gitconfig-mode)
(ph/install-package 'gitignore-mode)

diff-hl-mode highlights uncommitted changes on the left side of the window, allows you to jump between and revert them selectively.

(ph/install-package 'diff-hl)

Indentation

(setq-default indent-tabs-mode nil
              tab-width 2)

Rainbow

Rainbow-Mode sets background color to strings that match color names, e.g. #0000ff is displayed in white with a blue background

(ph/install-package 'rainbow-mode)
(add-hook 'prog-mode-hook 'rainbow-mode)

YASnippet

YASnippet is a template system for Emacs.

(ph/install-package 'yasnippet)
(ph/install-package 'yasnippet-snippets)
(require 'yasnippet)
(yas-global-mode 1)

C++

Irony-mode is an Emacs minor-mode that aims at improving the editing experience for the C, C++ and Objective-C languages. I

(ph/install-package 'irony)

(add-hook 'c++-mode-hook 'irony-mode)
(add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options)

Add flycheck linting.

(ph/install-package 'flycheck-irony)
(add-hook 'flycheck-mode-hook #'flycheck-irony-setup)
(ph/install-package 'flycheck-clang-tidy)
(eval-after-load 'flycheck
  '(add-hook 'flycheck-mode-hook #'flycheck-clang-tidy-setup))

Realgud - An extensible, modular GNU Emacs front-end for interacting with external debuggers (e.g. gdb).

(ph/install-package 'realgud)
(require 'realgud)

Notes:

  • Run irony-install-server the first time (and install OS dependencies).
  • Compile with M-x compile.
  • For debugging, use M-x gdb and M-x gdb-many-windows and the GUD keybindings (C-x C-a ...).
  • Use M-x realgud:gdb for debugging.

CSS

(setq css-indent-offset 2)

Go

Install on system.

$ go get -u github.com/rogpeppe/godef
$ go get -u github.com/mdempsky/gocode
(ph/install-package 'go-mode)

Autocompletion

(ph/install-package 'company-go)

Haml

(ph/install-package 'haml-mode)

Haskell

OS setup

curl -sSL https://get.haskellstack.org/ | sh

Create a new project

stack new myproject # to create a new project
# see stack --help

Intero is a complete interactive development program for Haskell. It offers many useful functions (see here) and an (automatically loaded) company-backend.

(ph/install-package 'intero)
(with-eval-after-load 'haskell-mode (intero-global-mode))

Don’t jump to the repl everytime it does something.

(defun ph/intero-repl-switch-back (&rest _)
  (intero-repl-switch-back))
(advice-add 'intero-repl-eval-region :after 'ph/intero-repl-switch-back)
(advice-add 'intero-repl-load        :after 'ph/intero-repl-switch-back)

Some more convenient keybindings

(general-def 'haskell-mode-map
  "C-c C-d" 'haskell-hoogle)
(general-def 'intero-mode-map
  "C-c C-b" 'intero-repl)
(general-def 'intero-repl-mode-map
  "C-c C-b" 'intero-repl-switch-back)

Syntax checker

(ph/install-package 'flycheck-haskell)
(add-hook 'haskell-mode-hook #'flycheck-haskell-setup)

JavaScript

Indentation

(setq js-indent-level 2)

Autocompletion

TODO: company-tern is not on melpa anymore, find a solution

;(ph/install-package 'company-tern)
;(require 'company-tern)
;(add-to-list 'company-backends 'company-tern)

jquery-doc provides completion source for auto-complete and company-mode as well as a jquery-doc command to lookup documentation.

(ph/install-package 'jquery-doc)

JS2-mode

(ph/install-package 'js2-mode)
(ph/install-package 'js2-refactor)

Tern

This is Tern. Tern is a stand-alone, editor-independent JavaScript analyzer that can be used to improve the JavaScript integration of existing editors.

(ph/install-package 'tern)

Put a file .tern-project in the root of the project. Additionally, a file ~/.tern-config.

A Ruby on Rails .tern-project may look like this:

{
  "libs": [
    "browser",
    "jquery"
  ],
  "loadEagerly": [
    "app/assets/javascripts/**/*.js",
    "lib/assets/javascript/**/*.js",
    "vendor/assets/javascript/**/*.js"
  ],
  "plugins": {
    "es_modules": {},
    "node": {}
  }
}

And my ~/.tern-config

{
  "libs": [
    "browser",
    "jquery"
  ],
  "plugins": {
    "es_modules": {},
    "node": {}
  }
}

Eslint

Use eslint instead of jshint.

(setq-default flycheck-disabled-checkers (append flycheck-disabled-checkers
                      '(javascript-jshint)))
(flycheck-add-mode 'javascript-eslint 'web-mode)

JSON

(ph/install-package 'json-mode)

Lua

(ph/install-package 'lua-mode)

Markdown

(ph/install-package 'markdown-mode)
(setq markdown-command "pandoc")

PHP

(ph/install-package 'php-mode)

Python

Elpy

(ph/install-package 'elpy)
(elpy-enable)

Indentation

(setq python-indent 2)

Some useful commands:

  • M-x run-python to start a shell
  • C-c C-z to switch to shell
  • C-c C-y b to send buffer to shell
  • many more send to shell functions

Auto Completion

Jedi is a Python auto-completion package for Emacs.

(ph/install-package 'jedi)
(add-hook 'python-mode-hook 'jedi:setup)
(setq jedi:complete-on-dot t)

Pipenv

There are several tools and helpers to handle virtual environments, dependencies, etc. (virtualenv, pyenv, pyenv-virtualenv, virtualenvwrapper, pyenv-virtualenvwrapper, pipenv, venv, pip-tools, …).

Pipenv is the newest and combines Pipfile, pip and virtualenv and plays well with projectile.

(ph/install-package 'pipenv)
(add-hook 'python-mode 'pipenv-mode)

To run a shell, use M-x pipenv-shell. Some example commands are:

pipenv --python 3.7 # create project with python 3.7
pipenv run python main.py # run application
pipenv install numpy # with Pipfile
pipenv install -r path/to/requirements.txt # with requirements.txt

Emacs speaks Statistics

Emacs Speaks Statistics (ess) is designed to support editing of scripts and interaction with various statistical analysis programs such as R, S-Plus, SAS, Stata and OpenBUGS/JAGS.

(ph/install-package 'ess)

Function:

  • C-c C-b to eval buffer
  • C-c C-j to eval line
  • C-c C-r to eval region
  • C-c C-f to eval function
  • And more

Ruby

Inf-ruby

Inf-Ruby provides a REPL buffer connected to a Ruby subprocess.

(ph/install-package 'inf-ruby)

Ruby Mode

Use the built-in ruby-mode for all common ruby-files.

No magic comments

(setq ruby-insert-encoding-magic-comment nil)

Robe

Robe is a code assistance tool that uses a Ruby REPL subprocess with your application or gem code loaded, to provide information about loaded classes and modules, and where each method is defined.

(ph/install-package 'robe)

Add the following gems to the Gemfile (if existent) and install them.

group :development do
  gem 'pry'
  gem 'pry-doc'
  gem 'method_source'
end

Generally, you’ll want to start with M-x inf-ruby-console-auto. If there’s no Ruby console running, most interactive commands provided by Robe will offer to launch it automatically.

The exceptions are code completion and eldoc, which only work if the server is already running. To launch it, type M-x robe-start.

As you change the code in your project, you’ll want to update the running process. To load the current file, type C-c C-l (ruby-load-file), see inf-ruby for more commands. When you’re working on a Rails project, you can type C-c C-k instead to reload the whole environment at once.

(add-hook 'ruby-mode-hook 'robe-mode)

Some useful Commands/Keybindings

  • C-c C-d Lookup documentation
  • M-. / M-,​ Jump to defintion and back

Use company mode for code completion.

(eval-after-load 'company
  '(push 'company-robe company-backends))

Rspec-mode

Rspec-Mode provides some convenience functions for dealing with RSpec.

(ph/install-package 'rspec-mode)

When you’ve hit the breakpoint, hit C-x C-q to enable inf-ruby.

(add-hook 'after-init-hook 'inf-ruby-switch-setup)

Usage:

  • C-c , s Verify the example or method defined at point
  • C-c , m Run all specs related to the current buffer
  • C-c , a Run spec for entire project
  • and more

Put the following in the Gemfile of the projects.

group :development do
  gem 'spring-commands-rspec'
end

Projectile Rails

Projectile Rails is a minor mode for working with Ruby on Rails applications and engines in GNU Emacs. Internally it is based on Projectile.

(setq projectile-rails-keymap-prefix (kbd "C-c p n"))
(ph/install-package 'projectile-rails)
(projectile-rails-global-mode)

Autocompletion

(ph/install-package 'company-inf-ruby)
(add-to-list 'company-backends 'company-inf-ruby)

Shell

Indentation

(setq sh-basic-offset 2)
(setq sh-indentation 2)

Autocompletion

(ph/install-package 'company-shell)
(add-to-list 'company-backends 'company-shell)

TeX

AUCTeX is an extensible package for writing and formatting TeX files in GNU Emacs.

(ph/install-package 'auctex)

Parse on load and save. This increases performance, especially for large multifile projects. The information is stored in an “auto” subdirectory.

(setq TeX-parse-self t)
(setq TeX-auto-save t)

Query to find out which is the master file.

(setq-default TeX-master nil)

Auctex by default adds comment sections to all the .tex files to save the TeX-master variable. In order not to confuse my colleagues with such auctex-specific lines I use per-directory local variables. For that I put the following list in a .dir-locals.el file in the project root if the main latex file is report.tex.

((latex-mode . ((TeX-master . "report"))))

I use Evince to view my PDFs.

(setq TeX-PDF-mode t)
(setq TeX-view-program-selection '((output-pdf "Evince")))

Sync with evince. Use Control + Left Click for backward search.

(add-hook 'LaTeX-mode-hook 'TeX-source-correlate-mode)
(setq TeX-source-correlate-start-server t)

Autocompletion

(ph/install-package 'company-bibtex)
(add-to-list 'company-backends 'company-bibtex)

(ph/install-package 'company-auctex)
(company-auctex-init)

Auto-fill

(add-hook 'LaTeX-mode-hook 'turn-on-auto-fill)

Web mode

Web-Mode is an autonomous emacs major-mode for editing web templates. HTML documents can embed parts (CSS / JavaScript) and blocks (client / server side).

(ph/install-package 'web-mode)

Use web-mode for the following file-types.

(add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.tag?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.vue?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.erb?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.js[x]?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.json?\\'" . web-mode))

Some web-mode settings.

(setq web-mode-markup-indent-offset 2
      web-mode-css-indent-offset 2
      web-mode-code-indent-offset 2
      web-mode-script-padding 2
      web-mode-style-padding 2
      web-mode-script-padding 2
      web-mode-block-padding 0
      web-mode-enable-current-element-highlight t
      web-mode-enable-current-column-highlight t)

Autocompletion

(ph/install-package 'company-web)
(require 'company-web-html)
(add-to-list 'company-backends 'company-web-html)

Use company backends for tern, html and css.

(add-hook 'web-mode-hook
          '(lambda ()
             (set (make-local-variable 'company-backends)
                  '(company-tern
                    company-web-html
                    company-css
                    company-files))))

Enable tern when the current language is JavaScript.

(advice-add 'company-tern :before
            '(lambda (&rest _)
               (if (equal major-mode 'web-mode)
                   (let ((web-mode-cur-language
                          (web-mode-language-at-pos)))
                     (if (or (string= web-mode-cur-language "javascript")
                             (string= web-mode-cur-language "jsx"))
                         (unless tern-mode (tern-mode))
                       (if tern-mode (tern-mode -1)))))))

Yaml

(ph/install-package 'yaml-mode)

Keybindings

My global keybindings are defined here. In order to get a better overview, they are neatly packed inside a minor-mode with its own keymap.

(defvar ph/global-keys-keymap (make-sparse-keymap))

(define-minor-mode ph/global-keys-mode
  "A minor mode with personalized keybindings."
  t ;; init-value
  nil ;; lighter
  ph/global-keys-keymap)

Not User-Reserved

Overwriting sequences (and defining new ones) for non-user-reserved sequences.

(general-def ph/global-keys-keymap
  "M-x"   'counsel-M-x
  "C-s"   'ph/swiper-from-isearch
  "C-h f" 'counsel-describe-function
  "C-h v" 'counsel-describe-variable)

(general-def ph/global-keys-keymap
  :prefix "C-x"
  "2"   'ph/vsplit-last-buffer
  "3"   'ph/hsplit-last-buffer
  "7"   'ph/open-last-buffer
  "b"   'ivy-switch-buffer
  "f"   'counsel-find-file
  "m"   'counsel-M-x
  "o"   'ace-window
  "C-b" 'ivy-switch-buffer
  "C-f" 'set-fill-column)

User-Reserved

User-reserved sequences (C-c followed by a letter and <F5> through <F9> without modifiers) should (theoretically -.-) not be used by any major modes and are intended for user-defined keybindings.

(general-def ph/global-keys-keymap
  :prefix "C-c"
  "d"   'crux-kill-line-backwards
  "f d" 'rg-dwim
  "f f" 'counsel-rg
  "f p" 'rg-project
  "f r" 'rg
  "h b" 'dumb-jump-back
  "h f" 'dumb-jump-go
  "h p" 'dumb-jump-go-prompt
  "l g" 'diff-hl-mode
  "l k" 'ph/kill-other-buffers
  "l m" 'magit-diff-buffer-file
  "l o" 'ph/xdg-open
  "l q" 'ph/qs-notes
  "l r" 'browse-kill-ring
  "l u" 'ph/unfill-paragraph
  "l v" 'visual-line-mode
  "i"   'indent-region
  "j"   'switch-to-next-buffer
  "k"   'switch-to-prev-buffer
  "m"   'magit
  "o"   'ph/occur-dwim
  "r"   'ph/mu4e
  "s b" 'flyspell-buffer
  "s c" 'flyspell-correct-at-point
  "s e" 'flyspell-mode
  "s n" 'flyspell-goto-next-error
  "s l" 'ph/ispell-next-dictionary
  "u"   'counsel-imenu
  "w m" 'which-key-show-major-mode
  "w t" 'which-key-show-top-level)

(general-def ph/global-keys-keymap
  "<f5>"  'ph/next-theme
  "<f6>"  'ivy-resume)

State Specific

Some state specific bindings.

(general-def 'normal ph/global-keys-keymap
  "SPC" 'god-local-mode)
(general-def 'visual ph/global-keys-keymap
  "SPC" 'god-local-mode)

(general-def 'motion ph/global-keys-keymap
  "j" 'evil-next-visual-line
  "k" 'evil-previous-visual-line)

(general-def 'insert ph/global-keys-keymap
  "<C-tab>" 'company-yasnippet
  "C-SPC"   'company-complete)

User Interface

Default Face

(set-face-attribute 'default nil
                    :family "DejaVu Sans Mono"
                    ;; some similar characters: 0OD yprw VU Ss |iIl
                    :weight 'normal
                    :height 120
                    :width 'normal)

Diminish

Diminish implements hiding or abbreviation of the mode line displays (lighters) of minor-modes.

(ph/install-package 'diminish)

(diminish 'counsel-mode)
(diminish 'god-local-mode)
(diminish 'ivy-mode)
(diminish 'projectile-mode)
(diminish 'undo-tree-mode)
(diminish 'visual-line-mode "VL")
(diminish 'which-key-mode)

Doom Modeline

Doom Modeline is a fancy and fast mode-line with minimalism design.

(ph/install-package 'doom-modeline)
(require 'doom-modeline)

Settings

Show both line and column numbers but not a percentage.

(setq line-number-mode t)
(setq column-number-mode t)
(setq doom-modeline-percent-position nil)

Make modeline as high as its content.

(setq doom-modeline-height 30)
(setq doom-modeline-bar-width 1)

No icons in my mode-line.

(setq doom-modeline-icon (display-graphic-p))
(setq all-the-icons-scale-factor 1.0)

Make the mode-line segment show the minions-menu AND the minor-modes.

(setq doom-modeline-minor-modes t)

Show flycheck info/warning/error instead of only one number for them all.

(setq doom-modeline-checker-simple-format nil)

Show long branch names.

(setq doom-modeline-vcs-max-length 24)

Show buffer-name instead of path.

(setq doom-modeline-buffer-file-name-style 'buffer-name)

Segments

A Segment to indicate the active ispell dictionary in flyspell.

(doom-modeline-def-segment ph/flyspell-dictionary
  (when (and flyspell-mode
             ispell-local-dictionary)
    (concat
     (doom-modeline-spc)
     (propertize ispell-local-dictionary
                 'face 'doom-modeline-buffer-minor-mode))))

Show the tramp method.

(doom-modeline-def-segment tramp-method
  (let ((method (file-remote-p default-directory 'method)))
    (if method
      (concat
       (doom-modeline-spc)
       (propertize method 'face 'doom-modeline-urgent)))))

Show the projectile project name.

(doom-modeline-def-segment ph/projectile-project-name
  (when (projectile-project-p)
    (concat
     (doom-modeline-spc)
     (propertize (projectile-project-name) 'face
                 (if (doom-modeline--active)
                     'doom-modeline-buffer-path
                   'mode-line-inactive))
     (doom-modeline-spc))))

Show the buffer name.

(doom-modeline-def-segment ph/buffer-info
  (concat
   (doom-modeline-spc)
   (propertize (buffer-name) 'face 'doom-modeline-buffer-file)))

Handle evil-state faces in a more extendable way. I also want them to be always active/colorful to have a better overview of where my windows start and end. Also the modals segment adds a second indicator for god-mode which I don’t want.

(setq doom-modeline-evil-state-faces-alist
      '((normal . doom-modeline-evil-normal-state)
        (emacs . doom-modeline-evil-emacs-state)
        (insert . doom-modeline-evil-insert-state)
        (motion . doom-modeline-evil-motion-state)
        (visual . doom-modeline-evil-visual-state)
        (operator . doom-modeline-evil-operator-state)
        (replace . doom-modeline-evil-replace-state)))

(doom-modeline-def-segment ph/evil-state
  "The current evil state. Requires `evil-mode' to be enabled."
  (when (bound-and-true-p evil-local-mode)
    (let ((tag (evil-state-property evil-state :tag t)))
      (propertize (if (stringp tag) tag (funcall tag)) 'face
                  (if (doom-modeline--active)
                      (cdr (assoc evil-state
                                  doom-modeline-evil-state-faces-alist))
                    'doom-modeline-bar-inactive)))))

(set-face-attribute 'doom-modeline-evil-emacs-state nil
                    :foreground "#111"
                    :background "SkyBlue")
(set-face-attribute 'doom-modeline-evil-insert-state nil
                    :foreground "#111"
                    :background "LimeGreen")
(set-face-attribute 'doom-modeline-evil-motion-state nil
                    :foreground "#111"
                    :background "#9c91e4")
(set-face-attribute 'doom-modeline-evil-normal-state nil
                    :foreground "#111"
                    :background "#ffdd30")
(set-face-attribute 'doom-modeline-evil-operator-state nil
                    :foreground "#111"
                    :background "NavajoWhite")
(set-face-attribute 'doom-modeline-evil-visual-state nil
                    :foreground "#111"
                    :background "#aaaaaa")
(set-face-attribute 'doom-modeline-evil-replace-state nil
                    :foreground "#111"
                    :background "#fd971f")

Setup

Setup the mode-line.

(doom-modeline-def-modeline 'main
  '(bar
    window-number
    ph/evil-state
    matches ; e.g. evil-substitute
    selection-info
    tramp-method
    ph/buffer-info
    remote-host
    buffer-position
    ph/flyspell-dictionary
    checker)
  '(minor-modes
    media-info
    misc-info
    vcs
    ph/projectile-project-name
    major-mode)
)

Last but not least, enable it.

(doom-modeline-mode 1)

When I open my first frame, the mode-line has a rather large empty space on the right side. The width is set incorrectly although doom-modeline-refresh-font-width-cache is in after-make-frame-functions. Adding the function to buffer-list-update-hook seems to resolve this issue.

(add-hook 'buffer-list-update-hook 'doom-modeline-refresh-font-width-cache)

Color Themes

Function to rotate through ph/theme-list. The entries may be functions or themes.

(ph/install-package 'doom-themes)

(setq ph/theme-list
      '(doom-peacock
        doom-tomorrow-day))

(defun ph/next-theme ()
  "Load next theme."
  (interactive)
  (setq ph/theme-list
        (ph/call-rotate 'ph/load-theme ph/theme-list)))

(add-hook 'emacs-startup-hook 'ph/next-theme)

Functions to load and customize themes.

(defun ph/load-theme (theme)
 "Like load-theme but first disable all custom-enabled themes ,
then load THEME and finally do some customizations."
  (interactive
   (list
    (intern (completing-read
             "Load custom theme: "
             (mapcar 'symbol-name (custom-available-themes))))))
  (mapcar 'disable-theme custom-enabled-themes)
  (load-theme theme t)
  (ph/customize-theme theme)
  (ph/any-theme-customize))

(defun ph/customize-theme (theme)
  "Call ph/THEME-customize if existent."
  (let ((fn (intern (concat "ph/" (symbol-name theme) "-customize"))))
    (message (concat "ph/" (symbol-name theme) "-customize"))
    (if (functionp fn)
        (funcall fn))))

(defun ph/doom-peacock-customize ()
  ;; highlight current line number
  (set-face-attribute 'line-number-current-line nil
                      :foreground "#BCD42A"
                      :weight 'bold))

(defun ph/any-theme-customize ()
  "This function sets some default values for all themes."

  ;; Never ever scale org and markdown headings
  (set-face-attribute 'org-level-1 nil
                      :height 1.0 :background nil)
  (set-face-attribute 'org-level-2 nil
                      :height 1.0 :background nil)
  (set-face-attribute 'org-level-3 nil
                      :height 1.0 :background nil)
  (set-face-attribute 'org-level-4 nil
                      :height 1.0 :background nil)
  (set-face-attribute 'org-level-5 nil
                      :height 1.0 :background nil)
  (set-face-attribute 'org-level-6 nil
                      :height 1.0 :background nil)
  (set-face-attribute 'org-level-7 nil
                      :height 1.0 :background nil)
  (set-face-attribute 'org-level-8 nil
                      :height 1.0 :background nil)

  (set-face-attribute 'doom-modeline-bar nil
                      :foreground nil
                      :background nil)

  ;; Use a smaller font for the mode-line
  (set-face-attribute 'mode-line nil
                      :height 90)
  (set-face-attribute 'mode-line-inactive nil
                      :height 90)
)

Fancy stuff

Disable fancy GUI stuff

(setq inhibit-splash-screen t)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(menu-bar-mode -1)

Frame Title

Show the buffer-name in the frame title and use the same title for unfocused frames.

(setq ph/frame-title-format '("%b"))
(setq frame-title-format ph/frame-title-format)
(setq icon-title-format ph/frame-title-format)

Tooltips

Don’t use ugly GTK tooltips.

(setq x-gtk-use-system-tooltips nil)

Releases

No releases published

Packages

No packages published