Skip to content

Instantly share code, notes, and snippets.

@zflat
Last active August 12, 2024 18:47
Show Gist options
  • Save zflat/bf17281acab1645029034817ec347b20 to your computer and use it in GitHub Desktop.
Save zflat/bf17281acab1645029034817ec347b20 to your computer and use it in GitHub Desktop.
Emacs configuration for productivity tools magit, org and others

Emacs Init File

This is a configuration file used to customize Emacs. To use this file, ensure the prerequisites are met and then follow the steps in the initial setup instructions section below to download this file from the upstream gist to a file named config.org in your emacs config directory.

Once the config.org file is in your emacs config directory and the setup is complete, you will be able to run Emacs with the customizations defined in the emacs configuration details section of the config.org file. Further customizatons and tweaks can be made by editing the appropriate sections of ~/.emacs.d/config.org.

Table of Contents

Prerequisites

Install Emacs if it is not already installed

Install using the Ubuntu PPA
sudo apt-add-repository ppa:ubuntu-elisp/ppa
sudo apt update
sudo apt install emacs-snapshot

Setup and usage

Create early-init.el and init.el files

Run the following commands in a bash terminal;

mkdir -p ${HOME}/.emacs.d/
echo "(setq package-enable-at-startup nil)" > ${HOME}/.emacs.d/early-init.el
tee ${HOME}/.emacs.d/init.el > /dev/null <<EOF
(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))
(setq straight-use-package-by-default t)

; User org to load the rest of the config
(straight-use-package 'org)
(org-babel-load-file (or
                      (getenv "EMACS_CONFIG_FILE")
                      (locate-user-emacs-file "config.org")))
EOF
ln -sf $(readlink -f ${HOME}/.emacs.d/) ~/.config/emacs

For more information about the init files and the user-specific config directory see these resources:

Download this config file into ~/.emacs.d/, your Emacs config directory

There are two options for obtaining this config file. The first is to download a copy and the second is to clone the git repo.

(Option 1) Download a copy

Either run the following command in a bash terminal;

wget -O "${HOME}/.emacs.d/config.org" https://gist.githubusercontent.com/zflat/bf17281acab1645029034817ec347b20/raw/init-productivity-emacs.org

(Option 2) Clone

Or, clone into your emacs config directory and create a symlink from the cloned location:

git clone https://gist.githubusercontent.com/zflat/bf17281acab1645029034817ec347b20.git "${HOME}/.emacs.d/init-productivity"
ln -s "${HOME}/.emacs.d/init-productivity/init-productivity-emacs.org" "${HOME}/.emacs.d/config.org"

Bootstrap packages before first starting Emacs (optional)

The first time you run Emacs with your new config, packages will need to be downloaded and built. You can optionally run your init file from the terminal before starting Emacs for the first time with your new config.

emacs -Q --batch --eval='(load-file (locate-user-emacs-file "init.el"))'

Let the process finish and then open Emacs as usual.

Usage

No additional steps are required to configure Emacs because the init.el file does all the work.

Just open “Emacs (Client)” from your application menu provided by the emacsclient.desktop entry.

Suggested bash alias

For convenience, define the following alias in your ~/.bashrc file to open Emacs in the current terminal to the git status buffer.

alias magit='emacsclient --alternate-editor= --tty --eval "(magit-status)" --eval "(delete-other-windows)"'

Further customization

Further customization and tweaks to your configuration is an iterative process. Here are some helpful ways to iterate on your configuration.

Copy your config to a temporary file and run Emacs using the new config file

If you copy your config for editing to a file located at ~/test_config.org you can run a new Emacs instance with that configuration by running the following from your terminal.

export EMACS_CONFIG_FILE=~/test_config.org
emacs -q -l init.el
Reload your config within a running instance of Emacs

It is possible to test loading this file from within Emacs while it is running. Place the point after the following Emacs-lisp expression and execute it with C-x C-e.

(org-babel-load-file (buffer-file-name (current-buffer)))

Strategies for speeding up Emacs loading

  • Run Emacs in server/client mode so that a daemon process performs the initialization and then new client instances open quickly.
    • Running a new client instance:
      • (Option 1) Either use the emacsclient.desktop entry from your applications menu
      • (Option 2) Or add an alias to open in client mode (and start the daemon automatically if not already started)
        alias em='emacsclient --alternate-editor= --create-frame --eval'
                    
    • Starting the daemon automatically
      • Set up a systemd service to start automatically
        systemctl --user enable emacs.service
                    
        • Use systemctl unit commands stop, start, restart to manage the Emacs daemon process
  • Byte compile the init and config files. See https://github.com/frap/emacs-literate/blob/master/lisp/compile.el and the accompanying init.el file.

Emacs Configuration Details

Packages installation

  • Custom packages are installed by strait.el which is bootstrapped in the initialization file.
    (straight-use-package '(asdf :host github :type git :repo "tabfugnic/asdf.el"))
    (straight-use-package '(dirvish :type git :host github :repo "alexluigit/dirvish"))
    (straight-use-package 'avy)
    (straight-use-package 'beacon)
    (straight-use-package 'cmake-font-lock)
    (straight-use-package 'cmake-mode)
    (straight-use-package 'counsel)
    (straight-use-package 'counsel-projectile)
    (straight-use-package 'diminish)
    (straight-use-package 'docker)
    (straight-use-package 'dockerfile-mode)
    (straight-use-package 'editorconfig)
    (straight-use-package 'elscreen)
    (straight-use-package 'emmet-mode)
    (straight-use-package 'expand-region)
    (straight-use-package 'flx)
    (straight-use-package 'ivy)
    (straight-use-package 'ivy-hydra)
    (straight-use-package 'js2-mode)
    (straight-use-package 'literate-calc-mode)
    (straight-use-package 'magit)
    (straight-use-package 'move-dup)
    (straight-use-package 'neotree)
    (straight-use-package 'projectile)
    (straight-use-package 'qml-mode)
    (straight-use-package 'spacemacs-theme)
    (straight-use-package 'swiper)
    (straight-use-package 'telephone-line)
    (straight-use-package 'toc-org)
    (straight-use-package 'web-mode)
    (straight-use-package 'yaml-mode)
        

Appearance

Set the font to use. Modify name and size to get the text to display how you want. This file specifies the default monospace font used in Ubuntu.

(let* ((name "DejaVu Sans Mono")
       (size "12"))
  (if (find-font (font-spec :name name))
    (set-frame-font (concat name " " size))
    (message (concat name " not found"))))

Disable the toolbar, and scrollbar and startup screen

(if (boundp 'tool-bar-mode) (tool-bar-mode -1))
(if (boundp 'scroll-bar-mode)
    (scroll-bar-mode -1)
  (if (boundp 'toggle-scroll-bar)
      (toggle-scroll-bar -1)))
  (if (boundp 'horizontal-scroll-bar-mode)
      (horizontal-scroll-bar-mode -1)
    (if (boundp 'toggle-horizontal-scroll-bar)
        (toggle-horizontal-scroll-bar -1)))
(setq inhibit-startup-screen t)
(setq inhibit-startup-message t)

Start Emacs maximized. If you wish to maximize right away during startup, then add this to early-init.el.

(add-to-list 'default-frame-alist '(fullscreen . maximized))

Disable the menu bar. Instead, open the menu with C-<mouse-3> or M-x menu-bar-open

(if (boundp 'menu-bar-mode) (menu-bar-mode -1))
  • Show the current directory in the frame bar (with a little humor too)
    (setq frame-title-format '("♥♥♥♡ " (:eval default-directory)))
        

Making buffer names unique https://www.gnu.org/software/emacs/manual/html_node/emacs/Uniquify.html

(setq uniquify-buffer-name-style 'post-forward-angle-brackets)

Highlight the line at point to make navigation more visible

(global-hl-line-mode t) 
(set-face-underline hl-line-face nil)
(require 'paren)
(show-paren-mode t)
(setq show-paren-delay 0)

Theme and colors

(require 'beacon)
(beacon-mode 1)

(setq telephone-line-lhs
      '((accent    . (telephone-line-buffer-segment))))
(setq telephone-line-primary-left-separator 'telephone-line-cubed-left
      telephone-line-secondary-left-separator 'telephone-line-cubed-hollow-left
      telephone-line-primary-right-separator 'telephone-line-cubed-right
      telephone-line-secondary-right-separator 'telephone-line-cubed-hollow-right)
(setq telephone-line-rhs
      '((accent    . (telephone-line-misc-info-segment))
        (nil   . (telephone-line-airline-position-segment))))

(setq telephone-line-height 18)
(telephone-line-mode 1)

(defun load-theme-spacemacs-dark ()
  (interactive)
  (progn
    (require `spacemacs-theme)
    (load-theme 'spacemacs-dark t)
    (face-spec-set 'bm-face '((t (:foreground "gold" :overline nil))))
    (setq beacon-color "LightGoldenrod3")
    (set-face-background 'avy-goto-char-timer-face (face-background 'menu))
    (set-face-foreground 'avy-goto-char-timer-face (face-foreground 'link)))
  (when (fboundp 'sml/setup) (sml/setup)))

Editor Navigation

  • Move cursor to different Panes by Arrow
    (when (fboundp 'windmove-default-keybindings)
      (windmove-default-keybindings))
        
  • Basic buffer switching capabilities
    (defun display-prev-buffer-in-window ()
      "Changes to the most recent buffer in the current window"
      (interactive)
      (apply 'set-window-buffer-start-and-point
             (append (list (selected-window))
                     (nth 0 (window-prev-buffers (selected-window))))))
    (global-set-key (kbd "<f1>") 'display-prev-buffer-in-window)
    (global-set-key (kbd "M-o") 'switch-to-buffer)
        
  • Advanced editor navigation using avy
    (require 'avy)
    (global-set-key (kbd "M-s") 'avy-goto-char)
    (global-set-key (kbd "C-;") 'avy-goto-char-timer)
    (setq avy-background t)
        

Editor completion UI

Emacs has minimal autocomplete and options selection by default so a handful of packages are required to get a rich user experience. The packages ivy, swiper and counsel work well together.

(require 'counsel)
(require 'swiper)

(require 'flx) ; scoring mechanism from flx is used by ivy–regex-fuzzy
(require 'ivy-hydra)
(require 'ivy)
(ivy-mode 1)
; see https://oremacs.com/2016/01/06/ivy-flx/
(setq ivy-re-builders-alist
      '((swiper . ivy--regex-plus)
        (counsel-projectile-find-file . ivy--regex-plus)
        (t . ivy--regex-fuzzy)))
(setq ivy-format-function 'ivy-format-function-line)
(setq ivy-use-virtual-buffers t) ;; see also https://emacs.stackexchange.com/questions/36836/how-to-remove-files-from-recentf-ivy-virtual-buffers
(setq ivy-virtual-abbreviate 'full) ;; helps to know files are from recentf instead of an open buffer
(global-set-key (kbd "C-s") 'counsel-grep-or-swiper)

(global-set-key (kbd "M-x") 'counsel-M-x)
(global-set-key (kbd "C-x C-f") 'counsel-find-file)
(defun wrapped-ivy-immediate-done (&rest ignore)
    (interactive)
    (if ( > (minibuffer-depth) 0) (ivy-immediate-done) nil))
(global-set-key (kbd "C-c C-f") 'wrapped-ivy-immediate-done) ;; useful for creating a new file
(global-set-key (kbd "C-c C-r") 'ivy-resume)

(require 'counsel-projectile)
(counsel-projectile-mode)

Editing shorcuts

Highlight the work at point (and more) with the keybinding C-=

(require 'expand-region)
(global-set-key (kbd "C-=") 'er/expand-region)

Perform line duplication and movement with ease

(require 'move-dup)
(global-set-key (kbd "M-<up>")       'move-dup-move-lines-up)
(global-set-key (kbd "ESC <up>")       'move-dup-move-lines-up)
(global-set-key (kbd "M-<down>")     'move-dup-move-lines-down)
(global-set-key (kbd "ESC <down>")     'move-dup-move-lines-down)
(global-set-key (kbd "C-S-d <down>") 'move-dup-duplicate-down)
(global-set-key (kbd "ESC C-<down>") 'move-dup-duplicate-down)
(global-set-key (kbd "C-S-d <up>")   'move-dup-duplicate-up)
(global-set-key (kbd "ESC C-<up>")   'move-dup-duplicate-up)

Author HTML tags like a pro with emmet

(require 'emmet-mode)
(add-hook 'web-mode-hook  'emmet-mode)

Productivity packages

Magit

The magit package make you a git power user!Open the magit status buffer with C-x g. Use C-u C-x g to select a different git repository than where the current buffer is contained.

(require 'magit)
  (global-set-key (kbd "C-x g") 'magit-status)
  (defun magit-display-buffer-scratch-full (buffer)
    (if (eq (current-buffer) (get-buffer "*scratch*"))
        (magit-display-buffer-fullframe-status-v1 buffer)
      (magit-display-buffer-traditional buffer)))
  (setq magit-display-buffer-function 'magit-display-buffer-scratch-full)
(setq magit-completing-read-function 'ivy-completing-read)

Projectile

The projectile package is a powerful way for Emacs to scope functionality to a “project” based on the active buffer. Since the granularity is of the active buffer, a single Emacs instance can be used with different projects at the same time depending on what buffer is being visited. Note: Invalidate Projectile cache with C-c p i

(require 'projectile)
(projectile-mode)
(setq projectile-enable-caching t)

File explorer

  • File tree functionality using neotree
    (require 'neotree)
    (global-set-key [f7] 'neotree-find)
    (global-set-key (kbd "<S-f7>") 'neotree-toggle)
        
  • File manager using the dirvish package
    (dirvish-override-dired-mode)
        

Virtual environment management tools

  • Manage scripting tool nodejs/python/ruby/etc versions from within Emacs (requires asdf to be installed)
    (require 'asdf)
        
  • Manage docker from within Emacs
    (require 'docker)
    (global-set-key (kbd "C-c d") 'docker)
        

Literate calc

Use the literate-calc-minor-mode to turn a buffer into a scratch pad for calculations. Enable by running M-x literate-calc-minor-mode.

Try out this example in a buffe with the minor mode enabled:

a = 40
b = 60
= a + b

Additional packag configurations

  • Emacs looks incapable without syntax highlighting to common file types. Add packages to handle any file types that you encounter.
    (require 'cmake-font-lock)
    (require 'cmake-mode)
    (setq auto-mode-alist
          (append
           '(("CMakeLists\\.txt\\'" . cmake-mode))
           '(("\\.cmake\\'" . cmake-mode))
           auto-mode-alist))
    
    (require 'dockerfile-mode)
    (add-to-list 'auto-mode-alist '("Dockerfile\\'" . dockerfile-mode))
    (require 'qml-mode)
    (autoload 'qml-mode "qml-mode" "Editing Qt Declarative." t)
    (add-to-list 'auto-mode-alist '("\\.qml$" . qml-mode))
    (add-hook 'qml-mode-hook #'(lambda ()
                                (js2-mode-exit)
                                (message "js2-mode exited")))
    (require 'js2-mode)
    ;; Enable js2-mode like this instead of with js-mode-hook so that
    ;; qml-mode, which uses js-mode does not also include js2-mode. May
    ;; need to add more file extensions in the future, but for now it is
    ;; nice that js2-mode is not enabled for JSON files too.
    (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
    (require 'yaml-mode)
    (add-to-list 'auto-mode-alist '("\\.yml$'" . yaml-mode))
    (add-to-list 'auto-mode-alist '("\\.yaml$'" . yaml-mode))
    (require 'web-mode)
        

Window managment

  • Elscreen for gnu-screen like window management
    (require 'elscreen)
    (elscreen-start)
    (setq elscreen-display-tab nil)
    (global-set-key (kbd "C-z C-z") 'elscreen-toggle)
        

Emacs system setup

  • Coding systems
    (setq locale-coding-system 'utf-8)
    (set-terminal-coding-system 'utf-8)
    (set-keyboard-coding-system 'utf-8)
    (set-selection-coding-system 'utf-8)
    (prefer-coding-system 'utf-8)
        
    • Editorconfig keeps tabs and spaces consistent because not everybody uses the same editor and same configuration. So lets use editorconfig so that Emacs plays nice with other editors.
      (require 'editorconfig)
      (editorconfig-mode 1)
              
  • Autosave files directly into the visited file after a timeout (instead of auto-saving to a temporary file). Also save buffers after they lose focus. This is useful when editing a file and then switching to a terminal or other application to run and test the changes.
    (setq auto-save-visited-interval 30)
    (auto-save-visited-mode 1)
    (add-function :after after-focus-change-function
                  (lambda ()
                    (unless (frame-focus-state) (save-some-buffers t nil))))
        
  • Disable Emacs from saving its state for the next time it is opened. For a minimal Emacs setup casual use, this is likely desirable but for primary Emacs use this should be re-enabled.
    (desktop-save-mode -1)
        
  • Automatically update buffer contents when a file is updated outside of emacs
    (auto-revert-mode t)
        
  • All indentation made with spaces
    (setq-default indent-tabs-mode nil)
    (setq tab-width 2)
        
  • Standard selection-highlighting behavior of other editors.
    (transient-mark-mode t)
        
  • Electric pair mode provides a way to easily insert matching delimiters
    (if (or (>= emacs-major-version 25)
            (and (>= emacs-major-version 24)
                 (>= emacs-minor-version 4)))
        (electric-pair-mode 1)) ; 
        

Post-initialization

  • Install and load the toc-org package after org mode is loaded. This is the package that automatically generates an up to date table of contents for us. See https://github.com/frap/emacs-literate/blob/master/readme.org for another example.
    (require 'toc-org)
    (add-hook 'org-mode-hook #'toc-org-enable)
        
  • Set up diminish after all modes have finished initializing.
  • To list minor modes: M-x describe-mode
    (require 'diminish)
    (diminish 'auto-revert-mode)
    (diminish 'abbrev-mode "Abv")
    (diminish 'auto-complete-mode)
    (diminish 'company-mode)
    (diminish 'projectile)
    (diminish 'projectile-mode)
    (diminish 'yas-minor-mode)
    (diminish 'editorconfig-mode)
    (diminish 'ivy-mode)
    (diminish 'flycheck-mode)
    (diminish 'eldoc-mode)
    (diminish 'ggtags-mode)
    (diminish 'beacon-mode)
    (diminish 'emmet-mode)
    (diminish 'god-local-mode)
        
  • Load your desired theme after initialization is complete
    (add-hook 'after-init-hook #'(lambda ()
                                   (load-theme-spacemacs-dark)))
    (load-theme-spacemacs-dark)
        
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment