Skip to content

Latest commit

 

History

History
4032 lines (3335 loc) · 135 KB

config.org

File metadata and controls

4032 lines (3335 loc) · 135 KB

Emacs Configuration

Table of Contents

About

Welcome to my literate Emacs configuration file.

This configuration was inspired by (read: large swathes copied from)

Initialize Emacs

Packages Sources

Configure package sources.

(add-to-list 'package-archives
             '("melpa" . "http://melpa.org/packages/") t)

use-package

To ease package installation use-package is necessary so willl need to be installed if it isn’t already.

(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

(use-package delight :ensure t)
(use-package use-package-ensure-system-package :ensure t)

Set environment variables from path

Make sure Emacs uses all the correct environment variables

(use-package exec-path-from-shell
  :ensure t
  :init
  (exec-path-from-shell-initialize))

Start Up

Better defaults

The following values are set for a better default user experience.

(setq-default
 ad-redefinition-action 'accept                   ; Silence warnings for redefinition
 cursor-in-non-selected-windows t                 ; Hide the cursor in inactive windows
 display-time-default-load-average nil            ; Don't display load average
 fill-column 80                                   ; Set width for automatic line breaks
 help-window-select t                             ; Focus new help windows when opened
 indent-tabs-mode nil                             ; Prefers spaces over tabs
 inhibit-startup-screen t                         ; Disable start-up screen
 initial-scratch-message ""                       ; Empty the initial *scratch* buffer
 kill-ring-max 128                                ; Maximum length of kill ring
 load-prefer-newer t                              ; Prefers the newest version of a file
 mark-ring-max 128                                ; Maximum length of mark ring
 read-process-output-max (* 1024 1024)            ; Increase the amount of data reads from the process
 scroll-conservatively most-positive-fixnum       ; Always scroll by one line
 select-enable-clipboard t                        ; Merge system's and Emacs' clipboard
 tab-width 4                                      ; Set width for tabs
 use-package-always-ensure t                      ; Avoid the :ensure keyword for each package
 user-full-name "Tobias Backer Dirks"             ; Set the full name of the current user
 user-mail-address "omgitsaheadcrab@gmail.com"    ; Set the email address of the current user
 display-line-numbers t                           ; Display line numbers on the left
 vc-follow-symlinks t                             ; Always follow the symlinks
 view-read-only t)                                ; Always open read-only buffers in view-mode
(cd "~/")                                         ; Move to the user directory
(column-number-mode 1)                            ; Show the column number
(display-time-mode 1)                             ; Enable time in the mode-line
(fset 'yes-or-no-p 'y-or-n-p)                     ; Replace yes/no prompts with y/n
(global-hl-line-mode)                             ; Hightlight current line
(set-default-coding-systems 'utf-8)               ; Default to utf-8 encoding
(show-paren-mode 1)                               ; Show the parent
(put 'narrow-to-region 'disabled nil)             ; Enable region narrowing/widening
(put 'upcase-region 'disabled nil)                ; Enable region upcasing
(put 'downcase-region 'disabled nil)              ; Enable region downcasing

Better file management with XDG

The XDG base directory specification is followed.

Be careful that GNU Emacs will not create the appropriate folders if they do not exist. Therefore, it is necessary to create them yourself:

mkdir ~/.cache/emacs ~/.local/share/emacs/

NOTE: the following XDG environment variables must be set in .profile.

(defvar xdg-bin (getenv "XDG_BIN_HOME")
  "The XDG bin base directory.")

(defvar xdg-cache (getenv "XDG_CACHE_HOME")
  "The XDG cache base directory.")

(defvar xdg-config (getenv "XDG_CONFIG_HOME")
  "The XDG config base directory.")

(defvar xdg-data (getenv "XDG_DATA_HOME")
  "The XDG data base directory.")

(defvar xdg-lib (getenv "XDG_LIB_HOME")
  "The XDG lib base directory.")

Customization

To avoid overloading init.el file generated by the user using the UI, the generated code is added to a separate file.

The XDG base directory specification is also followed for the auto-save-file folder.

(setq-default
 auto-save-list-file-name (expand-file-name (format "%s/emacs/auto-save-list" xdg-data))
 custom-file (expand-file-name (format "%s/emacs/custom.el" xdg-data)))
(when (file-exists-p custom-file)
  (load custom-file t))

Fonts

Set default font to Souce Code Pro Medium.

(set-face-attribute 'default nil :font "Source Code Pro Medium")
(set-fontset-font t 'latin "Noto Sans")

LSP

lsp-mode (Language Server Protocol) is used to minimize the required configuration for programming language support. Compatible servers can be found on the lsp-mode page.

(use-package lsp-mode
  :config
  (lsp-register-custom-settings
   '(("pylsp.plugins.pylsp_mypy.enabled" t t)
     ;; disable live mode to fix imports
     ("pylsp.plugins.pylsp_mypy.live_mode" nil t)
     ;; Disable these as they're duplicated by flake8
     ("pylsp.plugins.pycodestyle.enabled" nil t)
     ("pylsp.plugins.mccabe.enabled" nil t)
     ("pylsp.plugins.pyflakes.enabled" nil t)))
  :hook ((c-mode c++-mode dockerfile-mode java-mode json-mode python-mode rust-mode typescript-mode xml-mode yaml-mode) . lsp)
  :custom
  (lsp-clients-typescript-server-args '("--stdio" "--tsserver-log-file" "/dev/stderr"))
  (lsp-enable-folding nil)
  (lsp-enable-links nil)
  (lsp-enable-snippet nil)
  (lsp-enable-on-type-formatting nil)
  (lsp-prefer-flymake nil)
  (lsp-pylsp-plugins-flake8-enabled t)
  (lsp-clients-clangd-args '("-j=4" "-background-index" "-log=error" "--clang-tidy"))
  (lsp-session-file (expand-file-name (format "%s/emacs/lsp-session-v1" xdg-data)))
  (lsp-restart 'auto-restart)
  (lsp-rust-analyzer-server-display-inlay-hints t)
  (lsp-rust-analyzer-display-lifetime-elision-hints-enable "skip_trivial")
  (lsp-rust-analyzer-display-chaining-hints t)
  (lsp-rust-analyzer-display-lifetime-elision-hints-use-parameter-names nil)
  (lsp-rust-analyzer-display-closure-return-type-hints t)
  (lsp-rust-analyzer-display-parameter-hints nil)
  (lsp-rust-analyzer-display-reborrow-hints nil)
  )

  (use-package lsp-ui)

  (use-package dap-mode
  :defer
  :custom
  (dap-auto-configure-mode t                           "Automatically configure dap.")
  (dap-auto-configure-features
   '(sessions locals breakpoints expressions tooltip)  "Remove the button panel in the top.")
  :config
  ;;; dap for c++
  (require 'dap-lldb)

  ;;; dap for Rust
  (require 'dap-cpptools)

  ;;; set the debugger executable (c++)
  (setq dap-lldb-debug-program '("/usr/bin/lldb-vscode"))

  ;;; ask user for executable to debug if not specified explicitly (c++)
  (setq dap-lldb-debugged-program-function (lambda () (read-file-name "Select file to debug.")))

  ;;; default debug template for (c++)
  (dap-register-debug-template
   "C++ LLDB dap"
   (list :type "lldb-vscode"
         :cwd nil
         :args nil
         :request "launch"
         :program nil))

  (defun dap-debug-create-or-edit-json-template ()
    "Edit the C++ debugging configuration or create + edit if none exists yet."
    (interactive)
    (let ((filename (concat (lsp-workspace-root) "/launch.json"))
      (default "~/.config/emacs/dap-default-launch.json"))
      (unless (file-exists-p filename)
    (copy-file default filename))
      (find-file-existing filename))))

Theming

All The Icons

(use-package all-the-icons
  :if (display-graphic-p)
  ;; :init (all-the-icons-install-fonts t)
  :config
  (add-to-list 'all-the-icons-mode-icon-alist
               '(vterm-mode all-the-icons-octicon "terminal" :v-adjust 0.2))
  (add-to-list 'all-the-icons-icon-alist
               '("\\.xpm$" all-the-icons-octicon "file-media" :v-adjust 0.0 :face all-the-icons-dgreen))

  (add-to-list 'all-the-icons-icon-alist
               '("\\.toml$" all-the-icons-octicon "settings" :v-adjust 0.0 :face all-the-icons-dyellow))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(conf-toml-mode all-the-icons-octicon "settings" :v-adjust 0.0 :face all-the-icons-dyellow))
  (add-to-list 'all-the-icons-icon-alist
               '("\\.lua$" all-the-icons-fileicon "lua" :face all-the-icons-dblue))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(lua-mode all-the-icons-fileicon "lua" :face all-the-icons-dblue))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(help-mode all-the-icons-faicon "info-circle" :height 1.1 :v-adjust -0.1 :face all-the-icons-purple))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(helpful-mode all-the-icons-faicon "info-circle" :height 1.1 :v-adjust -0.1 :face all-the-icons-purple))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(Info-mode all-the-icons-faicon "info-circle" :height 1.1 :v-adjust -0.1))
  (add-to-list 'all-the-icons-icon-alist
               '("NEWS$" all-the-icons-faicon "newspaper-o" :height 0.9 :v-adjust -0.2))
  (add-to-list 'all-the-icons-icon-alist
               '("Cask\\'" all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.2 :face all-the-icons-blue))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(cask-mode all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.2 :face all-the-icons-blue))
  (add-to-list 'all-the-icons-icon-alist
               '(".*\\.ipynb\\'" all-the-icons-fileicon "jupyter" :height 1.2 :face all-the-icons-orange))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(ein:notebooklist-mode all-the-icons-faicon "book" :face all-the-icons-orange))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(ein:notebook-mode all-the-icons-fileicon "jupyter" :height 1.2 :face all-the-icons-orange))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(ein:notebook-multilang-mode all-the-icons-fileicon "jupyter" :height 1.2 :face all-the-icons-orange))
  (add-to-list 'all-the-icons-icon-alist
               '("\\.epub\\'" all-the-icons-faicon "book" :height 1.0 :v-adjust -0.1 :face all-the-icons-green))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(nov-mode all-the-icons-faicon "book" :height 1.0 :v-adjust -0.1 :face all-the-icons-green))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(gfm-mode all-the-icons-octicon "markdown" :face all-the-icons-lblue)))

;; While we're at it: Make dired, ‘dir’ectory ‘ed’itor, look pretty
(use-package all-the-icons-dired
:hook (dired-mode . all-the-icons-dired-mode))

Pretty Symbols

(use-package pretty-mode
  :config
  (global-pretty-mode 0))

Theme

Use nord-theme with doom-modeline.

NOTE: to be able to see icons in the doom-modeline, you will need to install all-the-icons.

(use-package doom-themes
  :config (load-theme 'doom-gruvbox t))

(use-package doom-modeline
  :defer 0.1
  :config (doom-modeline-mode))

(use-package fancy-battery
  :after doom-modeline
  :hook (after-init . fancy-battery-mode))

(use-package solaire-mode
  :custom (solaire-mode-remap-fringe t)
  :config
  (solaire-global-mode +1))

Disable graphical UI elements.

(menu-bar-mode -1)              ; Disable the menu bar
(scroll-bar-mode -1)            ; Disable the scroll bar
(tool-bar-mode -1)              ; Disable the tool bar
(tooltip-mode -1)              ; Disable the tooltips

Languages

C++

In order to have a fast and stable environment, I recommend using LSP as a client for LSP servers ccls and as server.

To use ccls with GNU Emacs, you must first install it with the package manager of your operating system.

  (use-package ccls
    :after projectile
    :ensure-system-package ccls
    :custom
    (ccls-args nil)
    (ccls-executable (executable-find "ccls"))
    (projectile-project-root-files-top-down-recurring
     (append '("compile_commands.json" ".ccls")
             projectile-project-root-files-top-down-recurring))
    :config (add-to-list 'projectile-globally-ignored-directories ".ccls-cache"))

  (setq c-basic-offset 4)
(eval-after-load "cc-mode"
  (progn
    (require 'cc-mode)
    (put 'c-electric-paren 'delete-selection nil)
    (put 'c-electric-brace 'delete-selection nil)))

To allow ccls to know the dependencies of your .cpp files with your .h files, it is important to provide an compile.commands.json file (or a .ccls file) at the root of your project.

For this, nothing could be easier. If like me you use a CMakeLists.txt file for all your C++ projects, then you just need to install the cmake package on your operating system and to generate the compile.commands.json file, you have to do:

cmake -H. -BDebug -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=YES
ln -s Debug/compile_commands.json

Code formatting with clang-format

  (use-package clang-format)
;    :after ccls)

Google style

(use-package google-c-style
  :hook ((c-mode c++-mode) . google-set-c-style)
  (c-mode-common . google-make-newline-indent)
  :commands google-set-c-style)

CMake

CMake is a cross-platform build system generator.

(use-package cmake-mode
  :mode ("CMakeLists\\.txt\\'" "\\.cmake\\'"))

(use-package cmake-font-lock
  :after (cmake-mode)
  :hook (cmake-mode . cmake-font-lock-activate))

CSS – LESS – SCSS

In order to have a fast and stable environment, I recommend using LSP as a client for LSP servers and vscode-css-languageserver-bin as server.

(use-package css-mode
  :custom (css-indent-offset 2))

(use-package less-css-mode
  :mode "\\.less\\'")

(use-package scss-mode
  :mode "\\.scss\\'")

CSV

(use-package csv-mode)

Docker

I like to use Docker when I need to install various databases or other services that only work on a particular operating system while keeping my operating system clean.

(use-package dockerfile-mode
  :delight "δ "
  :mode "Dockerfile\\'")

Lisp

Emacs Lisp

(use-package elisp-mode :ensure nil :delight "ξ ")

Eldoc

Provides minibuffer hints when working with Emacs Lisp.

(use-package eldoc
  :delight
  :hook (emacs-lisp-mode . eldoc-mode))

Common Lisp

For arch install the sbcl and quicklisp packages.

sudo pacman -S sbcl
yay -S quicklisp
echo '(load "/usr/lib/quicklisp/setup")' > ~/.sbclrc

Then within sbcl install SLIME

(quicklisp-quickstart:install)
(ql:quickload "quicklisp-slime-helper")
(if (string-equal system-type "darwin")
  (progn
    (print "FIXME: CommonLisp (ros) on MacOS"))
    ;(add-to-list 'exec-path "/usr/local/bin/")
    ;(load (expand-file-name "~/.roswell/helper.el")))
  (progn
    (load (expand-file-name "/usr/lib/quicklisp/slime-helper.el"))
    ;; Replace "sbcl" with the path to your implementation
    (setq inferior-lisp-program "sbcl")))

Scheme

For arch install the guile package.

sudo pacman -S guile
(use-package geiser
  :config
  (setq geiser-active-implementations '(guile)))

HTML

In order to have a fast and stable environment, I recommend using LSP as a client for LSP servers and vscode-html-languageserver as server.

To use vscode-html-languageserver with GNU Emacs, you must first install it with the package manager of your operating system.

Let’s configure emmet-mode, to produce HTML from CSS-like selector:

(use-package emmet-mode
  :delight
  :hook (css-mode sgml-mode web-mode))

INI

ini-mode does a good job of handling .ini files.

(use-package ini-mode
  :defer 0.4
  :mode ("\\.ini\\'"))

Java

In order to have a fast and stable environment, I recommend using lsp-java as LSP client and Eclipse JDT Language Server as LSP server.

NOTE: before configuring lsp-java, don’t forget to configure lsp-mode.

Let’s define the LSP client to use the LSP server:

(use-package lsp-java
  :after lsp
  :hook (java-mode . lsp)
  :custom (lsp-java-server-install-dir
           (expand-file-name (format "%s/eclipse.jdt.ls/server" xdg-lib))))
(require 'dap-java)

Gradle

Most of my Java projects are made with gradle. The configuration is easy enough:

(use-package gradle-mode
  :mode ("\\.java\\'" "\\.gradle\\'")
  :bind (:map gradle-mode-map
              ("C-c C-c" . gradle-build)
              ("C-c C-t" . gradle-test))
  :preface
  (defun my/switch-to-compilation-window ()
    "Switches to the *compilation* buffer after compilation."
    (other-window 1))
  :config
  (advice-add 'gradle-build :after #'my/switch-to-compilation-window)
  (advice-add 'gradle-test :after #'my/switch-to-compilation-window))

JavaScript

For my JavaScript configuration, I took my sources from the Nicolas Petton’s blog which I found very well explained.

Setting up Emacs for JavaScript (part #1) Setting up Emacs for JavaScript (part #2)

I like to use prettier to get my TypeScript code clean. To use it, don’t forget to install it with your package manager.

(use-package prettier-js
  :delight
  :custom (prettier-js-args '("--print-width" "100"
                              "--single-quote" "true"
                              "--trailing-comma" "all")))

js2-mode

By default, GNU Emacs uses js-mode as major mode for JavaScript buffers and I prefer use js2-mode instead because of his abilities to parses buffers and builds an AST for things like syntax highlighting.

(use-package js2-mode
  :hook ((js2-mode . js2-imenu-extras-mode)
         (js2-mode . prettier-js-mode))
  :mode "\\.js\\'"
  :custom (js-indent-level 2))

js2-refactor

Provides powerful refactoring based on the AST generated by js2-mode.

(use-package js2-refactor
  :bind (:map js2-mode-map
              ("C-k" . js2r-kill)
              ("M-." . nil))
  :hook ((js2-mode . js2-refactor-mode)
         (js2-mode . (lambda ()
                       (add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t))))
  :config (js2r-add-keybindings-with-prefix "C-c C-r"))

xref-js2

Makes it easy to jump to function references or definitions.

(use-package xref-js2 :defer 5)

tern

Parses JavaScript files in a project and makes type inference to provide meaningful completion (with type clues) and cross-reference support.

Unfortunately, tern has some problems with cross-references that explain why I am using xref-js2 instead.

(use-package tern
  :ensure-system-package (tern . "npm install -g tern")
  :bind (("C-c C-c" . compile)
         :map tern-mode-keymap
         ("M-." . nil))
  :hook ((js2-mode . company-mode)
         (js2-mode . tern-mode)))

Then, add a .tern-project file to the root of your project.

Here is an example configuration for a project that uses requirejs and jQuery, without taking into account of the bower_components directory:

{
    "libs": [
        "jquery"
    ],
    "loadEagerly": [
        "./**/*.js"
    ],
    "dontLoad": [
        "./bower_components/"
    ],
    "plugins": {
        "requirejs": {
            "baseURL": "./"
        }
    }
}

JSON

JSON is used a lot, especially in the web. Therefore, it is important to have a decent configuration to feel comfortable when handling such files.

(use-package json-mode
  :delight "J "
  :mode "\\.json\\'"
  :hook (before-save . my/json-mode-before-save-hook)
  :preface
  (defun my/json-mode-before-save-hook ()
    (when (eq major-mode 'json-mode)
      (json-pretty-print-buffer)))

  (defun my/json-array-of-numbers-on-one-line (encode array)
    "Prints the arrays of numbers in one line."
    (let* ((json-encoding-pretty-print
            (and json-encoding-pretty-print
                 (not (cl-loop for x across array always (numberp x)))))
           (json-encoding-separator (if json-encoding-pretty-print "," ", ")))
      (funcall encode array)))
  :config (advice-add 'json-encode-array :around #'my/json-array-of-numbers-on-one-line))

LaTeX

I use LaTeX for my reports, CVs, summaries, etc.

(use-package tex
  :ensure auctex
  :bind (:map TeX-mode-map
              ("C-c C-o" . TeX-recenter-output-buffer)
              ("C-c C-l" . TeX-next-error)
              ("M-[" . outline-previous-heading)
              ("M-]" . outline-next-heading))
  :hook (LaTeX-mode . reftex-mode)
  :preface
  (defun my/switch-to-help-window (&optional ARG REPARSE)
    "Switches to the *TeX Help* buffer after compilation."
    (other-window 1))
  :custom
  (TeX-auto-save t)
  (TeX-byte-compile t)
  (TeX-clean-confirm nil)
  (TeX-master 'dwim)
  (TeX-parse-self t)
  (TeX-PDF-mode t)
  (TeX-source-correlate-mode t)
  (TeX-view-program-selection '((output-pdf "PDF Tools")))
  :config
  (advice-add 'TeX-next-error :after #'my/switch-to-help-window)
  (advice-add 'TeX-recenter-output-buffer :after #'my/switch-to-help-window)
  ;; the ":hook" doesn't work for this one... don't ask me why.
  (add-hook 'TeX-after-compilation-finished-functions 'TeX-revert-document-buffer))

(use-package bibtex
  :after auctex
  :hook (bibtex-mode . my/bibtex-fill-column)
  :preface
  (defun my/bibtex-fill-column ()
    "Ensures that each entry does not exceed 120 characters."
    (setq fill-column 120)))

(use-package company-auctex
  :after (auctex company)
  :config (company-auctex-init))

(use-package company-math :after (auctex company))

I want a TeX engine that can deal with Unicode and use any font I like.

(setq-default TeX-engine 'xetex)

reftex

Minor mode with distinct support for \label, \ref and \cite in LaTeX.

(use-package reftex
  :after auctex
  :custom
  (reftex-plug-into-AUCTeX t)
  (reftex-save-parse-info t)
  (reftex-use-multiple-selection-buffers t))

Lua

I rarely program in Lua, but when I do, lua-mode satisfies me amply.

(use-package lua-mode
  :delight "Λ "
  :mode "\\.lua\\'"
  :interpreter ("lua" . lua-mode))

Markdown

Before you can use this package, make sure you install pandoc on your operating system.

(use-package markdown-mode
  :ensure-system-package (pandoc . "yay -S pandoc")
  :delight "μ "
  :mode ("\\.markdown\\'" "\\.md\\'")
  :custom (markdown-command "/usr/bin/pandoc"))

(use-package markdown-preview-mode
  :after markdown-mode
  :custom
  (markdown-preview-javascript
   (list (concat "https://github.com/highlightjs/highlight.js/"
                 "9.15.6/highlight.min.js")
         "<script>
            $(document).on('mdContentChange', function() {
              $('pre code').each(function(i, block)  {
                hljs.highlightBlock(block);
              });
            });
          </script>"))
  (markdown-preview-stylesheets
   (list (concat "https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/"
                 "3.0.1/github-markdown.min.css")
         (concat "https://github.com/highlightjs/highlight.js/"
                 "9.15.6/styles/github.min.css")

         "<style>
            .markdown-body {
              box-sizing: border-box;
              min-width: 200px;
              max-width: 980px;
              margin: 0 auto;
              padding: 45px;
            }

            @media (max-width: 767px) { .markdown-body { padding: 15px; } }
          </style>")))

Python

I use pylsp as my LSP client. I use black to reformat my Python buffer. Before use it, don’t forget to install python-black in your system.

Also, you’ll need python-lsp-server[all], pylsp-black, pylsp-isort, pylsp-mypy, mypy, flake8.

To sort my Python imports, py-isort does a good job. Also, don’t forget to install python-isort in your system.

Automagically resolve imports (requires importmagic and epc)

pip install python-lsp-server[all]
pip install black black-macchiato debugpy epc flake8 futureimportmagic isort
pip install mypy pdbpp pyls-flake8 pyls-isort pyls-memestra pylsp-mypy
pip install pylsp-rope python-lsp-black

Let’s take a look to my Python configuration:

(use-package python
  :delight "π "
  :preface
  (defun python-remove-unused-imports()
    "Removes unused imports and unused variables with autoflake."
    (interactive)
    (if (executable-find "autoflake")
        (progn
          (shell-command (format "autoflake --remove-all-unused-imports -i %s"
                                 (shell-quote-argument (buffer-file-name))))
          (revert-buffer t t t))
      (warn "python-mode: Cannot find autoflake executable."))))

(use-package python-black
  :demand t
  :after python
  :config (setq python-black-extra-args '("-l 79")))

(use-package py-isort
  :after python
  :hook ((python-mode . pyvenv-mode)))

(use-package pyenv-mode
  :after python
  :hook ((python-mode . pyenv-mode)
         (projectile-switch-project . projectile-pyenv-mode-set))
  :custom (pyenv-mode-set "3.9.0")
  :preface
  (defun projectile-pyenv-mode-set ()
    "Set pyenv version matching project name."
    (let ((project (projectile-project-name)))
      (if (member project (pyenv-mode-versions))
          (pyenv-mode-set project)
        (pyenv-mode-unset)))))

(add-hook 'pyenv-mode-hook 'lsp)

;; Show flake8 errors in lsp-ui
(defun lsp-set-cfg ()
  (let ((lsp-cfg `(:pylsp (:configurationSources ("flake8")))))
    (lsp--set-configuration lsp-cfg)))

;; Activate that after lsp has started
;; (add-hook 'lsp-after-initialize-hook 'lsp-set-cfg)
(add-hook 'python-mode-hook 'lsp-set-cfg)

(use-package pyvenv
  :after python
  :hook ((python-mode . pyvenv-mode)
         (python-mode . (lambda ()
                          (if-let ((pyvenv-directory (find-pyvenv-directory (buffer-file-name))))
                              (pyvenv-activate pyvenv-directory))
                          (lsp))))
  :custom
  (pyvenv-default-virtual-env-name "env")
  (pyvenv-mode-line-indicator '(pyvenv-virtual-env-name ("[venv:"
                                                         pyvenv-virtual-env-name "]")))
  :preface
  (defun find-pyvenv-directory (path)
    "Checks if a pyvenv directory exists."
    (cond
     ((not path) nil)
     ((file-regular-p path) (find-pyvenv-directory (file-name-directory path)))
     ((file-directory-p path)
      (or
       (seq-find
        (lambda (path) (file-regular-p (expand-file-name "pyvenv.cfg" path)))
        (directory-files path t))
       (let ((parent (file-name-directory (directory-file-name path))))
         (unless (equal parent path) (find-pyvenv-directory parent))))))))

(use-package importmagic
  :config
  (add-hook 'python-mode-hook 'importmagic-mode))

(require 'dap-python)
(setq dap-python-debugger 'debugpy)

Rust

rustic is an extension of rust-mode which adds a number of useful features (see the its github readme) to it. It is the core of the setup and you can use just it without any other Emacs packages (and without rust-analyzer) if you just want code highlighting, compilation and cargo commands bound to emacs shortcuts, and a few other features.

(use-package rust-mode)

(use-package rustic
  :bind (:map rustic-mode-map
              ("M-j" . lsp-ui-imenu)
              ("M-?" . lsp-find-references)
              ("C-c C-c l" . flycheck-list-errors)
              ("C-c C-c a" . lsp-execute-code-action)
              ("C-c C-c r" . lsp-rename)
              ("C-c C-c q" . lsp-workspace-restart)
              ("C-c C-c Q" . lsp-workspace-shutdown)
              ("C-c C-c s" . lsp-rust-analyzer-status))
  :config
  ;; uncomment for less flashiness
  ;; (setq lsp-eldoc-hook nil)
  ;; (setq lsp-enable-symbol-highlighting nil)
  ;; (setq lsp-signature-auto-activate nil)

  ;; comment to disable rustfmt on save
  (setq rustic-format-on-save nil)
  (add-hook 'rustic-mode-hook 'rk/rustic-mode-hook))

(defun rk/rustic-mode-hook ()
  ;; so that run C-c C-c C-r works without having to confirm, but don't try to
  ;; save rust buffers that are not file visiting. Once
  ;; https://github.com/brotzeit/rustic/issues/253 has been resolved this should
  ;; no longer be necessary.
  (when buffer-file-name
    (setq-local buffer-save-without-query t)))

Shell-script

A recent thing when you create/edit a shell script file is to automatically grant it execution rights (with chmod +x).

The snippet below ensures that the execution right is automatically granted to save a shell script file that begins with a #! shebang:

(use-package sh-script
  :ensure nil
  :hook (after-save . executable-make-buffer-file-executable-if-script-p))

SQL

sql-indent gives me the possibility to easily manage .sql files.

(use-package sql-indent
  :after (:any sql sql-interactive-mode)
  :delight sql-mode "Σ ")

TypeScript

If you use GNU Emacs 27+, I recommend to use typescript-language-server as LSP server. After installed it with your package manager, you need to use typescript-mode to get the syntax color:

(use-package typescript-mode
  :mode ("\\.ts\\'" "\\.tsx\\'")
  :hook (typescript-mode . prettier-js-mode)
  :custom
  (add-hook 'typescript-mode-hook #'(lambda ()
                               (enable-minor-mode
                                '("\\.tsx?\\'" . prettier-js-mode)))))

XML

Unfortunately, XML is still used, especially for creating web services in SOAP. However, xml-mode exists to help us:

(use-package xml-mode
  :ensure nil
  :mode ("\\.wsdl\\'" "\\.xsd\\'"))

YAML

yaml-mode gives me the possibility to easily manage .yml files.

(use-package yaml-mode
  :delight "ψ "
  :mode "\\.yml\\'"
  :interpreter ("yml" . yaml-mode)
  :config
   (add-hook 'yaml-mode-hook
    '(lambda ()
      (define-key yaml-mode-map "\C-m" 'newline-and-indent))))

Advanced Configuration

Alert

Most packages use alerts to make notifications with libnotify. Don’t forget to first install a notification daemon, like dunst.

Alert is a Growl-workalike for Emacs which uses a common notification interface and multiple, selectable “styles”, whose use is fully customizable by the user.

John Wiegley

(use-package alert
  :defer 1
  :custom (alert-default-style 'libnotify))

Auto-Completion

company provides auto-completion at point and displays a small pop-in containing the candidates.

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.

Dmitry Gutov

(use-package company
  :defer 0.5
  :delight
  :custom
  (company-begin-commands '(self-insert-command))
  (company-idle-delay 0.5)
  (company-minimum-prefix-length 2)
  (company-show-numbers t)
  (company-tooltip-align-annotations 't)
  (company-dabbrev-downcase nil)
  (global-company-mode t))

I use company with company-box that allows a company front-end with icons.

(use-package company-box
  :after company
  :delight
  :hook (company-mode . company-box-mode))

Backups

It is important to have a stable backup environment. Don’t hesitate to save a lot.

NOTE: the functions defined below avoid running a bash command when saving certain files with GNU Emacs.

(use-package files
  :ensure nil
  :preface
  (defvar *afilename-cmd*
    `((,(format "%s/X11/Xresources" xdg-config) . ,(format "xrdb -merge %s/X11/Xresources" xdg-config))
      (,(format "%s/xbindkeysrc" (getenv "HOME")) . "xbindkeys -p"))
    "File association list with their respective command.")

  (defun my/cmd-after-saved-file ()
    "Execute a command after saved a specific file."
    (let* ((match (assoc (buffer-file-name) *afilename-cmd*)))
      (when match
        (shell-command (cdr match)))))
  :hook (after-save . my/cmd-after-saved-file)
  :custom
  (backup-directory-alist `(("." . ,(expand-file-name (format "%s/emacs/backups/" xdg-data)))))
  (delete-old-versions -1)
  (vc-make-backup-files t)
  (version-control t))

Buffers

Buffers can quickly become a mess. For some people, it’s not a problem, but I like being able to find my way easily.

(use-package ibuffer
  :bind ("C-x C-b" . ibuffer)
  :config
  (setq ibuffer-saved-filter-groups
        '(("default"
           ("ERC" (mode . erc-mode))
           )))
  (add-hook 'ibuffer-mode-hook
            #'(lambda ()
               (ibuffer-switch-to-saved-filter-groups "default"))))

(use-package ibuffer-projectile
  :after ibuffer
  :preface
  (defun my/ibuffer-projectile ()
    (ibuffer-projectile-set-filter-groups)
    (unless (eq ibuffer-sorting-mode 'alphabetic)
      (ibuffer-do-sort-by-alphabetic)))
  :hook (ibuffer . my/ibuffer-projectile))

;; You probably don't want to see empty project groups
(setq ibuffer-show-empty-filter-groups nil)

Some buffers should not be deleted by accident:

(defvar *protected-buffers* '("*scratch*" "*Messages*")
  "Buffers that cannot be killed.")

(defun my/protected-buffers ()
  "Protects some buffers from being killed."
  (dolist (buffer *protected-buffers*)
    (with-current-buffer buffer
      (emacs-lock-mode 'kill))))

(add-hook 'after-init-hook #'my/protected-buffers)

Button Lock

Enable button lock mode

(use-package button-lock
  :config (global-button-lock-mode 1))
(defun test-link ()
  (interactive)
  (button-lock-set-button
   "test\\?name=[a-zA-Z0-9_\\.]*"
   (lambda ()
     (interactive)
     (browse-url (concat "http://localhost:8080/"
                         (buffer-substring
                          (previous-single-property-change (point) 'mouse-face)
                          (next-single-property-change (point) 'mouse-face)))))
   :face 'link
   :face-policy 'prepend
   :keyboard-binding "RET"))
(add-hook 'python-mode-hook
          (lambda ()  (test-link)))

Calculator

May be useful in a timely manner.

(use-package calc
  :defer t
  :custom
  (math-additional-units
   '((GiB "1024 * MiB" "Giga Byte")
     (MiB "1024 * KiB" "Mega Byte")
     (KiB "1024 * B" "Kilo Byte")
     (B nil "Byte")
     (Gib "1024 * Mib" "Giga Bit")
     (Mib "1024 * Kib" "Mega Bit")
     (Kib "1024 * b" "Kilo Bit")
     (b "B / 8" "Bit")))
  (math-units-table nil))

Calendar

Remembering all the dates is not obvious, especially since some varies every year. In order to remember each important date, I recorded the list of important dates according to my country, Belgium. It is very likely that some dates are different in your country, therefore, adapt the configuration below accordingly.

(use-package calendar
  :ensure nil
  :custom (calendar-mark-holidays-flag t))

(use-package holidays
  :ensure nil
  :custom
  (holiday-bahai-holidays nil)
  (holiday-christian-holidays
   '((holiday-fixed 1 6 "Epiphany")
     (holiday-fixed 2 2 "Candlemas")
     (holiday-easter-etc -47 "Mardi Gras")
     (holiday-easter-etc 0 "Easter Day")
     (holiday-easter-etc 1 "Easter Monday")
     (holiday-easter-etc 39 "Ascension")
     (holiday-easter-etc 49 "Pentecost")
     (holiday-fixed 8 15 "Assumption")
     (holiday-fixed 11 1 "All Saints' Day")
     (holiday-fixed 11 2 "Day of the Dead")
     (holiday-fixed 11 22 "Saint Cecilia's Day")
     (holiday-fixed 12 1 "Saint Eloi's Day")
     (holiday-fixed 12 4 "Saint Barbara")
     (holiday-fixed 12 6 "Saint Nicholas Day")
     (holiday-fixed 12 25 "Christmas Day")))
  (holiday-general-holidays
   '((holiday-fixed 1 1 "New Year's Day")
     (holiday-fixed 2 14 "Valentine's Day")
     (holiday-fixed 3 8 "International Women's Day")
     (holiday-fixed 10 31 "Halloween")
     (holiday-fixed 11 11 "Armistice of 1918")))
  (holiday-hebrew-holidays nil)
  (holiday-islamic-holidays nil)
  (holiday-local-holidays
   '((holiday-fixed 5 1 "Labor Day")
     (holiday-float 3 0 0 "Grandmothers' Day")
     (holiday-float 4 4 3 "Secretary's Day")
     (holiday-float 5 0 2 "Mother's Day")
     (holiday-float 6 0 3 "Father's Day")))
  (holiday-oriental-holidays nil))

Dashboard

Always good to have a dashboard.

(use-package dashboard
  :if (< (length command-line-args) 2)
  :preface
  (defun dashboard-load-packages (list-size)
    (insert (make-string (ceiling (max 0 (- dashboard-banner-length 38)) 5) ? )
            (format "%d packages loaded in %s" (length package-activated-list) (emacs-init-time))))
  :custom
  (dashboard-center-content t)
  (dashboard-items '((recents . 10)
                     (bookmarks . 2)
                     (projects . 4)
                     (agenda . 5)))
  (dashboard-set-file-icons t)
  (dashboard-set-heading-icons t)
  (dashboard-set-init-info nil)
  (dashboard-set-navigator t)
  (dashboard-startup-banner 'logo)
  :config
  (add-to-list 'dashboard-item-generators '(packages . dashboard-load-packages))
  (setq initial-buffer-choice (lambda () (get-buffer "*dashboard*")))
  (add-hook 'dashboard-mode-hook (lambda () (display-line-numbers-mode -1)))
  (dashboard-setup-startup-hook))

Deft

Deft is an Emacs mode for quickly browsing, filtering, and editing directories of plain text notes.

(use-package deft
  :bind ("<f8>" . deft)
  :init (setq deft-directory "~/Documents/org/notes/"
              deft-extensions '("org" "md" "txt")
              deft-default-extension "org"
              deft-recursive t
              deft-auto-save-interval 0))

Dired

For those who didn’t know, GNU Emacs is also a file explorer.

(use-package dired
  :ensure nil
  :delight "Dired "
  :custom
  (dired-auto-revert-buffer t)
  (dired-dwim-target t)
  (dired-hide-details-hide-symlink-targets nil)
  (dired-listing-switches "-alh")
  (dired-ls-F-marks-symlinks nil)
  (dired-recursive-copies 'always))

(use-package dired-narrow
  :bind (("C-c C-n" . dired-narrow)
         ("C-c C-f" . dired-narrow-fuzzy)
         ("C-c C-r" . dired-narrow-regexp)))

(use-package dired-subtree
  :bind (:map dired-mode-map
              ("<backtab>" . dired-subtree-cycle)
              ("<tab>" . dired-subtree-toggle)))

EditorConfig

EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs.

(use-package editorconfig
  :defer 0.3
  :config (editorconfig-mode 1))

Ending Up

I’m using an .org file to maintain my GNU Emacs configuration. However, at its launch, it will load the config.el source file for a faster loading.

The code below, executes org-babel-tangle asynchronously when config.org is saved.

(use-package async)

(defvar *config-file* (expand-file-name "config.org" user-emacs-directory)
  "The configuration file.")

(defvar *config-last-change* (nth 5 (file-attributes *config-file*))
  "Last modification time of the configuration file.")

(defvar *show-async-tangle-results* nil
  "Keeps *emacs* async buffers around for later inspection.")

(defun my/config-updated ()
  "Checks if the configuration file has been updated since the last time."
  (time-less-p *config-last-change*
               (nth 5 (file-attributes *config-file*))))

(defun my/config-tangle ()
  "Tangles the org file asynchronously."
  (when (my/config-updated)
    (setq *config-last-change*
          (nth 5 (file-attributes *config-file*)))
    (my/async-babel-tangle *config-file*)))

(defun my/async-babel-tangle (org-file)
  "Tangles the org file asynchronously."
  (let ((init-tangle-start-time (current-time))
        (file (buffer-file-name))
        (async-quiet-switch "-q"))
    (async-start
     `(lambda ()
        (require 'org)
        (org-babel-tangle-file ,org-file))
     (unless *show-async-tangle-results*
       `(lambda (result)
          (if result
              (message "SUCCESS: %s successfully tangled (%.2fs)."
                       ,org-file
                       (float-time (time-subtract (current-time)
                                                  ',init-tangle-start-time)))
            (message "ERROR: %s as tangle failed." ,org-file)))))))

EPUB

(use-package nov
  :mode ("\\.epub\\'" . nov-mode)
  :custom (nov-text-width 75))

General

gnuplot

To generate a fast and quality graphic, gnuplot is perfect.

(use-package gnuplot
  :ensure-system-package gnuplot
  :defer 2)

(use-package gnuplot-mode
  :after gnuplot
  :mode "\\.gp\\'")

move-text

Moves the current line (or if marked, the current region’s, whole lines).

(use-package move-text
  :bind (("M-p" . move-text-up)
         ("M-n" . move-text-down))
  :config (move-text-default-bindings))

paradox

Improved GNU Emacs standard package menu.

Project for modernizing Emacs’ Package Menu. With improved appearance, mode-line information. Github integration, customizability, asynchronous upgrading, and more.

Artur Malabarba

(use-package paradox
  :defer 1
  :custom
  (paradox-column-width-package 27)
  (paradox-column-width-version 13)
  (paradox-execute-asynchronously t)
  (paradox-hide-wiki-packages t)
  :config
  (paradox-enable)
  (remove-hook 'paradox-after-execute-functions #'paradox--report-buffer-print))

rainbow-mode

Colorize colors as text with their value.

(use-package rainbow-mode
  :delight
  :hook (prog-mode))

Replace the current file with the saved one

Avoids call the function or reload Emacs.

(use-package autorevert
  :ensure nil
  :delight auto-revert-mode
  :bind ("C-x R" . revert-buffer)
  :custom (auto-revert-verbose nil)
  :config (global-auto-revert-mode 1))

try

Useful to temporary use a package.

(use-package try :defer 5)

undo-tree

GNU Emacs’s undo system allows you to recover any past state of a buffer. To do this, Emacs treats “undo itself as another editing that can be undone”.

(use-package undo-tree
  :delight
  :bind ("C--" . undo-tree-redo)
  :init (global-undo-tree-mode)
  :custom
  (undo-tree-visualizer-timestamps t)
  (undo-tree-visualizer-diff t)
  (undo-tree-history-directory-alist '(("." . "~/.config/emacs/undo"))))

web-mode

An autonomous emacs major-mode for editing web templates.

(use-package web-mode
  :delight ""
  :hook ((css-mode web-mode) . rainbow-mode)
  :mode (("\\.blade\\.php\\'" . web-mode)
         ("\\.html?\\'" . web-mode)
         ("\\.jsx\\'" . web-mode)
         ("\\.php$" . my/php-setup))
  :preface
  (defun enable-minor-mode (my-pair)
    "Enable minor mode if filename match the regexp."
    (if (buffer-file-name)
        (if (string-match (car my-pair) buffer-file-name)
            (funcall (cdr my-pair)))))
  :custom
  (web-mode-attr-indent-offset 2)
  (web-mode-block-padding 2)
  (web-mode-css-indent-offset 2)
  (web-mode-code-indent-offset 2)
  (web-mode-comment-style 2)
  (web-mode-enable-current-element-highlight t)
  (web-mode-markup-indent-offset 2))

(add-hook 'web-mode-hook #'(lambda ()
                             (enable-minor-mode
                              '("\\.js?\\'" . prettier-js-mode))))

(add-hook 'web-mode-hook #'(lambda ()
                             (enable-minor-mode
                              '("\\.jsx?\\'" . prettier-js-mode))))

(add-hook 'web-mode-hook #'(lambda ()
                             (enable-minor-mode
                              '("\\.ts?\\'" . prettier-js-mode))))


(setq web-mode-code-indent-offset                   2
      web-mode-markup-indent-offset                 2
      web-mode-css-indent-offset                    2
      web-mode-enable-html-entities-fontification   nil
      web-mode-enable-block-face                    nil
      web-mode-enable-comment-annotation            nil
      web-mode-enable-comment-interpolation         nil
      web-mode-enable-control-block-indentation     nil
      web-mode-enable-css-colorization              nil
      web-mode-enable-current-column-highlight      nil
      web-mode-enable-current-element-highlight     nil
      web-mode-enable-element-content-fontification nil
      web-mode-enable-heredoc-fontification         nil
      web-mode-enable-inlays                        nil
      web-mode-enable-optional-tags                 nil
      web-mode-enable-part-face                     nil
      web-mode-enable-sexp-functions                nil
      web-mode-enable-sql-detection                 nil
      web-mode-enable-string-interpolation          nil
      web-mode-enable-whitespace-fontification      nil
      web-mode-enable-auto-expanding                nil
      web-mode-enable-auto-indentation              nil
      web-mode-enable-auto-closing                  nil
      web-mode-enable-auto-opening                  nil
      web-mode-enable-auto-pairing                  nil
      web-mode-enable-auto-quoting                  nil)

which-key

It’s difficult to remember all the keyboard shortcuts. The which-key package helps to solve this.

I used guide-key in my past days, but which-key is a good replacement.

(use-package which-key
  :defer 0.2
  :delight
  :config (which-key-mode))

wiki-summary

It is impossible to know everything, which is why a quick description of a term, without breaking its workflow, is ideal.

(use-package wiki-summary
  :defer 1
  :preface
  (defun my/format-summary-in-buffer (summary)
    "Given a summary, sticks it in the *wiki-summary* buffer and displays
     the buffer."
    (let ((buf (generate-new-buffer "*wiki-summary*")))
      (with-current-buffer buf
        (princ summary buf)
        (fill-paragraph)
        (goto-char (point-min))
        (view-mode))
      (pop-to-buffer buf))))

(advice-add 'wiki-summary/format-summary-in-buffer :override #'my/format-summary-in-buffer)

Highlight indentation

Highlight the indentation is a feature that visually pleases me. Indeed, without having to count the spaces, I can see that the code is well indented.

(use-package highlight-indent-guides
  :defer 0.3
  :hook (prog-mode . highlight-indent-guides-mode)
  :custom (highlight-indent-guides-method 'character))

History

Provides the ability to have commands and their history saved so that whenever you return to work, you can re-run things as you need them. This is not a radical function, it is part of a good user experience.

(use-package savehist
  :ensure nil
  :custom
  (history-delete-duplicates t)
  (history-length t)
  (savehist-additional-variables '(kill-ring search-ring regexp-search-ring))
  (savehist-file (expand-file-name (format "%s/emacs/history" xdg-cache)))
  (savehist-save-minibuffer-history 1)
  :config (savehist-mode 1))

Hydra

Hydra allows me to display a list of all the commands implemented in the echo area and easily interact with them.

Once you summon the Hydra through the prefixed binding (the body + any one head), all heads can be called in succession with only a short extension.

The Hydra is vanquished once Hercules, any binding that isn’t the Hydra’s head, arrives. Note that Hercules, besides vanquishing the Hydra, will still serve his original purpose, calling his proper command. This makes the Hydra very seamless, it’s like a minor mode that disables itself auto-magically.

Oleh Krehel

(use-package hydra
  :bind (("C-c I" . hydra-image/body)
         ("C-c M" . hydra-merge/body)
         ("C-c T" . hydra-tool/body)
         ("C-c b" . hydra-btoggle/body)
         ("C-c c" . hydra-clock/body)
         ("C-c D" . dap-hydra)
         ("C-c e" . hydra-erc/body)
         ("C-c f" . hydra-flycheck/body)
         ("C-c g" . hydra-go-to-file/body)
         ("C-c m" . hydra-magit/body)
         ("C-c o" . hydra-org/body)
         ("C-c p" . hydra-projectile/body)
         ("C-c q" . hydra-query/body)
         ("C-c s" . hydra-spelling/body)
         ("C-c t" . hydra-pytest/body)
         ("C-c T" . hydra-tex/body)
         ("C-c u" . hydra-upload/body)
         ("C-c w" . hydra-windows/body)))

(use-package major-mode-hydra
  :after hydra
  :preface
  (defun with-alltheicon (icon str &optional height v-adjust)
    "Displays an icon from all-the-icon."
    (s-concat (all-the-icons-alltheicon icon :v-adjust (or v-adjust 0) :height (or height 1)) " " str))

  (defun with-faicon (icon str &optional height v-adjust)
    "Displays an icon from Font Awesome icon."
    (s-concat (all-the-icons-faicon icon :v-adjust (or v-adjust 0) :height (or height 1)) " " str))

  (defun with-fileicon (icon str &optional height v-adjust)
    "Displays an icon from the Atom File Icons package."
    (s-concat (all-the-icons-fileicon icon :v-adjust (or v-adjust 0) :height (or height 1)) " " str))

  (defun with-octicon (icon str &optional height v-adjust)
    "Displays an icon from the GitHub Octicons."
    (s-concat (all-the-icons-octicon icon :v-adjust (or v-adjust 0) :height (or height 1)) " " str)))

Hydra / BToggle

Group a lot of commands.

(pretty-hydra-define hydra-btoggle
  (:hint nil :color amaranth :quit-key "q" :title (with-faicon "toggle-on" "Toggle" 1 -0.05))
  ("Basic"
   (("a" abbrev-mode "abbrev" :toggle t)
    ("h" global-hungry-delete-mode "hungry delete" :toggle t))
   "Coding"
   (("e" electric-operator-mode "electric operator" :toggle t)
    ("F" flyspell-mode "flyspell" :toggle t)
    ("f" flycheck-mode "flycheck" :toggle t)
    ("l" lsp-mode "lsp" :toggle t)
    ("s" smartparens-mode "smartparens" :toggle t))
   "UI"
   (("i" ivy-rich-mode "ivy-rich" :toggle t))))

Hydra / Clock

Group clock commands.

(pretty-hydra-define hydra-clock
  (:hint nil :color teal :quit-key "q" :title (with-faicon "clock-o" "Clock" 1 -0.05))
  ("Action"
   (("c" org-clock-cancel "cancel")
    ("d" org-clock-display "display")
    ("e" org-clock-modify-effort-estimate "effort")
    ("i" org-clock-in "in")
    ("j" org-clock-goto "jump")
    ("o" org-clock-out "out")
    ("p" org-pomodoro "pomodoro")
    ("r" org-clock-report "report"))))

Hydra / ERC

Group ERC commands.

(pretty-hydra-define hydra-erc
  (:hint nil :color teal :quit-key "q" :title (with-faicon "comments-o" "ERC" 1 -0.05))
  ("Action"
   (("b" my/erc-browse-last-url "browse last url")
    ("c" my/erc-start-or-switch "connect")
    ("d" erc-quit-server "disconnect")
    ("j" erc-join-channel "join")
    ("n" erc-channel-names "names")
    ("o" my/erc-get-ops "ops")
    ("u" my/erc-count-users "users")
    ("r" my/erc-reset-track-mode "reset track mode"))))

Hydra / Flycheck

Group Flycheck commands.

(pretty-hydra-define hydra-flycheck
  (:hint nil :color teal :quit-key "q" :title (with-faicon "plane" "Flycheck" 1 -0.05))
  ("Checker"
   (("?" flycheck-describe-checker "describe")
    ("d" flycheck-disable-checker "disable")
    ("m" flycheck-mode "mode")
    ("s" flycheck-select-checker "select"))
   "Errors"
   (("<" flycheck-previous-error "previous" :color pink)
    (">" flycheck-next-error "next" :color pink)
    ("f" flycheck-buffer "check")
    ("l" flycheck-list-errors "list"))
   "Other"
   (("M" flycheck-manual "manual")
    ("v" flycheck-verify-setup "verify setup"))))

Hydra / Go To

Group jump-to-files commands.

(pretty-hydra-define hydra-go-to-file
  (:hint nil :color teal :quit-key "q" :title (with-faicon "file-text-o" "Go To" 1 -0.05))
  ("Agenda"
   (("ac" (find-file "~/Documents/org/agenda/contacts.org") "contacts")
    ("ao" (find-file "~/Documents/org/agenda/organizer.org") "organizer")
    ("ap" (find-file "~/Documents/org/agenda/people.org") "people")
    ("ar" (find-file "~/Documents/org/agenda/routine.org") "routine")
    ("aw" (find-file "~/Documents/org/agenda/work.org") "work"))
   "Config"
   (("ca" (find-file (format "%s/alacritty/alacritty.yml" xdg-config)) "alacritty")
    ("cA" (find-file (format "%s/sh/aliases" xdg-config)) "aliases")
    ("cd" (find-file (format "%s/dunst/dunstrc" xdg-config)))
    ("ce" (find-file (format "%s/emacs/config.org" xdg-config)) "emacs")
    ("cE" (find-file (format "%s/sh/environ" xdg-config)) "environ")
    ("ci" (find-file (format "%s/i3/config" xdg-config)) "i3")
    ("cn" (find-file (format "%s/neofetch/config.conf" xdg-config)) "neofetch")
    ("cs" (find-file (format "%s/sway/config" xdg-config)) "sway")
    ("ct" (find-file (format "%s/tmux/tmux.conf" xdg-config)) "tmux")
    ("cp" (find-file (format "%s/polybar/simple-black" xdg-config)) "polybar")
    ("cr" (find-file (format "%s/rofi/config.rasi" xdg-config)) "rofi")
    ("cx" (find-file (format "%s/sh/xdg" xdg-config)) "xdg"))
   "Other"
   (("ob" (find-file "~/Documents/org/other/books.org") "book")
    ("ol" (find-file "~/Documents/org/other/learning.org") "learning")
    ("om" (find-file "~/Documents/org/other/movies.org"))
    ("op" (find-file "~/Documents/org/other/purchases.org") "purchase")
    ("ou" (find-file "~/Documents/org/other/usb.org") "usb"))))

Hydra / Image

Group images commands.

(pretty-hydra-define hydra-image
  (:hint nil :color pink :quit-key "q" :title (with-faicon "file-image-o" "Images" 1 -0.05))
  ("Action"
   (("r" image-rotate "rotate")
    ("s" image-save "save" :color teal))
    "Zoom"
    (("-" image-decrease-size "out")
     ("+" image-increase-size "in")
     ("=" image-transform-reset "reset"))))

Hydra / Magit

Group Magit commands.

(pretty-hydra-define hydra-magit
  (:hint nil :color teal :quit-key "q" :title (with-alltheicon "git" "Magit" 1 -0.05))
  ("Action"
   (("b" magit-blame "blame")
    ("c" magit-clone "clone")
    ("i" magit-init "init")
    ("l" magit-log-buffer-file "commit log (current file)")
    ("L" magit-log-current "commit log (project)")
    ("s" magit-status "status"))))

Hydra / Merge

Group Merge commands.

(pretty-hydra-define hydra-merge
  (:hint nil :color pink :quit-key "q" :title (with-alltheicon "git" "Merge" 1 -0.05))
  ("Move"
   (("n" smerge-next "next")
    ("p" smerge-prev "previous"))
   "Keep"
   (("RET" smerge-keep-current "current")
    ("a" smerge-keep-all "all")
    ("b" smerge-keep-base "base")
    ("l" smerge-keep-lower "lower")
    ("u" smerge-keep-upper "upper"))
   "Diff"
   (("<" smerge-diff-base-upper "upper/base")
    ("=" smerge-diff-upper-lower "upper/lower")
    (">" smerge-diff-base-lower "base/lower")
    ("R" smerge-refine "redefine")
    ("E" smerge-ediff "ediff"))
   "Other"
   (("C" smerge-combine-with-next "combine")
    ("r" smerge-resolve "resolve")
    ("k" smerge-kill-current "kill current"))))

Hydra / Org

Group Org commands.

(pretty-hydra-define hydra-org
  (:hint nil :color teal :quit-key "q" :title (with-fileicon "org" "Org" 1 -0.05))
  ("Action"
   (("A" my/org-archive-done-tasks "archive")
    ("a" org-agenda "agenda")
    ("c" org-capture "capture")
    ("d" org-decrypt-entry "decrypt")
    ("i" org-insert-link-global "insert-link")
    ("j" my/org-jump "jump-task")
    ("k" org-cut-subtree "cut-subtree")
    ("o" org-open-at-point-global "open-link")
    ("p" my/org-refile-to-blog-post-dir "blog-post")
    ("r" org-refile "refile")
    ("s" org-store-link "store-link")
    ("t" org-show-todo-tree "todo-tree"))))

Hydra / Projectile

Group Projectile commands.

(pretty-hydra-define hydra-projectile
  (:hint nil :color teal :quit-key "q" :title (with-faicon "rocket" "Projectile" 1 -0.05))
  ("Buffers"
   (("b" counsel-projectile-switch-to-buffer "list")
    ("k" projectile-kill-buffers "kill all")
    ("S" projectile-save-project-buffers "save all"))
   "Find"
   (("d" counsel-projectile-find-dir "directory")
    ("D" projectile-dired "root")
    ("f" counsel-projectile-find-file "file")
    ("p" counsel-projectile-switch-project "project"))
   "Other"
   (("c" projectile-compile-project "compile project")
    ("i" projectile-invalidate-cache "reset cache")
    ("x" projectile-run-project "run project"))
   "Search"
   (("r" projectile-replace "replace")
    ("R" projectile-replace-regexp "regexp replace")
    ("s" counsel-rg "search"))))

Hydra / Query

Group Query commands.

(pretty-hydra-define hydra-query
  (:hint nil :color teal :quit-key "q" :title (with-faicon "search" "Engine-Mode" 1 -0.05))
  ("Query"
   (("a" engine/search-amazon "amazon")
    ("d" engine/search-duckduckgo "duckduckgo")
    ("g" engine/search-github "github")
    ("i" engine/search-google-images "google images")
    ("m" engine/search-google-maps "google maps")
    ("s" engine/search-stack-overflow "stack overflow")
    ("w" engine/search-wikipedia "wikipedia")
    ("y" engine/search-youtube "youtube"))))

Hydra / Spelling

Group spelling commands.

(pretty-hydra-define hydra-spelling
  (:hint nil :color teal :quit-key "q" :title (with-faicon "magic" "Spelling" 1 -0.05))
  ("Checker"
   (("c" langtool-correct-buffer "correction")
    ("C" langtool-check-done "clear")
    ("d" ispell-change-dictionary "dictionary")
    ("l" (message "Current language: %s (%s)" langtool-default-language ispell-current-dictionary) "language")
    ("s" my/switch-language "switch")
    ("w" wiki-summary "wiki"))
   "Errors"
   (("<" flyspell-correct-previous "previous" :color pink)
    (">" flyspell-correct-next "next" :color pink)
    ("f" langtool-check "find"))))

Hydra / (py)test

Group python-pytest commands.

(pretty-hydra-define hydra-pytest
  (:hint nil :color teal :quit-key "q" :title (with-alltheicon "python" "Pytest" 1 -0.05))
  ("Action"
   (("f" python-pytest-function-dwim "test function")
    ("a" python-pytest-file-dwim "test file")
    ("p" python-pytest "test project")
    ("m" python-pytest-dispatch "show menu"))))

Hydra / TeX

Group TeX commands.

(pretty-hydra-define hydra-tex
  (:hint nil :color teal :quit-key "q" :title (with-fileicon "tex" "LaTeX" 1 -0.05))
  ("Action"
   (("g" reftex-goto-label "goto")
    ("r" reftex-query-replace-document "replace")
    ("s" counsel-rg "search")
    ("t" reftex-toc "table of contents"))))

Hydra / Tool

Group Tool commands.

(pretty-hydra-define hydra-tool
  (:hint nil :color teal :quit-key "q" :title (with-faicon "briefcase" "Tool" 1 -0.05))
  ("Network"
   (("c" ipcalc "subnet calculator")
    ("i" ipinfo "ip info"))))

Hydra / TypeScript

Group TypeScript commands.

(defhydra hydra-typescript (:color blue)
  "
  ^
  ^TypeScript^          ^Do^
  ^──────────^──────────^──^───────────
  _q_ quit             _b_ back
  ^^                   _e_ errors
  ^^                   _j_ jump
  ^^                   _r_ references
  ^^                   _R_ restart
  ^^                   ^^
  "
  ("q" nil)
  ("b" tide-jump-back)
  ("e" tide-project-errors)
  ("j" tide-jump-to-definition)
  ("r" tide-references)
  ("R" tide-restart-server))

Hydra / Upload

Group upload commands.

(pretty-hydra-define hydra-upload
  (:hint nil :color teal :quit-key "q" :title (with-faicon "cloud-upload" "Upload" 1 -0.05))
  ("Action"
   (("b" webpaste-paste-buffe "buffer")
    ("i" imgbb-upload "image")
    ("r" webpaste-paste-region "region"))))

Hydra / Windows

Group window-related commands.

(pretty-hydra-define hydra-windows
  (:hint nil :forein-keys warn :quit-key "q" :title (with-faicon "windows" "Windows" 1 -0.05))
  ("Window"
   (("b" balance-windows "balance")
    ("i" enlarge-window "heighten")
    ("j" shrink-window-horizontally "narrow")
    ("k" shrink-window "lower")
    ("l" enlarge-window-horizontally "widen")
    ("s" switch-window-then-swap-buffer "swap" :color teal))
   "Zoom"
   (("-" text-scale-decrease "out")
    ("+" text-scale-increase "in")
    ("=" (text-scale-increase 0) "reset"))))

Icons

To integrate icons with doom-modeline, switch-to-buffer, counsel-find-file and many other functions; all-the-icons is just the best package that you can find.

NOTE: If it’s the first time that you install the package, you must run M-x all-the-icons-install-fonts.

(use-package all-the-icons
  :if (display-graphic-p)
  :config (unless (find-font (font-spec :name "all-the-icons"))
            (all-the-icons-install-fonts t)))

IRC

IRC is the best way for me to get a quick answer to a simple question and to learn from more competent people than me on a subject. I’d rather use erc than rcirc because I find rcirc very minimal.

Besides, for people like me, who want to store your password in a GPG file, you just need to specify a file priority list with auth-sources, to tell erc where to start looking for your password first.

Of course, don’t forget to add this line in your .authinfo.gpg file, where <nickname> and <password> match your login details.

machine irc.freenode.net login <nickname> password <password>

Then encrypt that file with gpg -c .authinfo and don’t forget to delete the .authinfo file.

Finally, specify to erc that you use a .authinfo file with: (setq erc-prompt-for-nickserv-password nil).

(use-package erc
  :delight "ε "
  :preface
  (defun my/erc-browse-last-url ()
    "Searchs backwards through an ERC buffer, looking for a URL. When a URL is
     found, it prompts you to open it."
    (interactive)
    (save-excursion
      (let ((ffap-url-regexp "\\(https?://\\)."))
        (ffap-next-url t t))))

  (defun my/erc-count-users ()
    "Displays the number of users and ops connected on the current channel."
    (interactive)
    (if (get-buffer "irc.freenode.net:6667")
        (let ((channel (erc-default-target)))
          (if (and channel (erc-channel-p channel))
              (let ((hash-table (with-current-buffer (erc-server-buffer)
                                  erc-server-users))
                    (users 0)
                    (ops 0))
                (maphash (lambda (k v)
                           (when (member (current-buffer)
                                         (erc-server-user-buffers v))
                             (cl-incf users))
                           (when (erc-channel-user-op-p k)
                             (cl-incf ops)))
                         hash-table)
                (message "%d users (%s ops) are online on %s" users ops channel))
            (user-error "The current buffer is not a channel")))
      (user-error "You must first be connected on IRC")))

  (defun my/erc-get-ops ()
    "Displays the names of ops users on the current channel."
    (interactive)
    (if (get-buffer "irc.freenode.net:6667")
        (let ((channel (erc-default-target)))
          (if (and channel (erc-channel-p channel))
              (let (ops)
                (maphash (lambda (nick cdata)
                           (if (and (cdr cdata)
                                    (erc-channel-user-op (cdr cdata)))
                               (setq ops (cons nick ops))))
                         erc-channel-users)
                (if ops
                    (message "The online ops users are: %s"  (mapconcat 'identity ops " "))
                  (message "There are no ops users online on %s" channel)))
            (user-error "The current buffer is not a channel")))
      (user-error "You must first be connected on IRC")))

  (defun my/erc-notify (nickname message)
    "Displays a notification message for ERC."
    (let* ((channel (buffer-name))
           (nick (erc-hl-nicks-trim-irc-nick nickname))
           (title (if (string-match-p (concat "^" nickname) channel)
                      nick
                    (concat nick " (" channel ")")))
           (msg (s-trim (s-collapse-whitespace message))))
      (alert (concat nick ": " msg) :title title)))

  (defun my/erc-preprocess (string)
    "Avoids channel flooding."
    (setq str (string-trim (replace-regexp-in-string "\n+" " " str))))

  (defun my/erc-reset-track-mode ()
    "Resets ERC track mode."
    (interactive)
    (setq erc-modified-channels-alist nil)
    (erc-modified-channels-update)
    (erc-modified-channels-display)
    (force-mode-line-update))

  (defun my/erc-start-or-switch ()
    "Connects to ERC, or switch to last active buffer."
    (interactive)
    (if (get-buffer "irc.freenode.net:6667")
        (erc-track-switch-buffer 1)
      (erc :server "irc.freenode.net" :port 6667 :nick "omgitsaheadcrab")))
  :hook ((ercn-notify . my/erc-notify)
         (erc-send-pre . my/erc-preprocess))
  :custom-face
  (erc-action-face ((t (:foreground "#8fbcbb"))))
  (erc-error-face ((t (:foreground "#bf616a"))))
  (erc-input-face ((t (:foreground "#ebcb8b"))))
  (erc-notice-face ((t (:foreground "#ebcb8b"))))
  (erc-timestamp-face ((t (:foreground "#a3be8c"))))
  :custom
  (erc-autojoin-channels-alist '(("freenode.net" "#archlinux" "#bash" "##c"
                                  "##c++" "#emacs""#i3" "#linux" "#notmuch"
                                  "#python")))
  (erc-autojoin-timing 'ident)
  (erc-fill-function 'erc-fill-static)
  (erc-fill-static-center 22)
  (erc-header-line-format "%n on %t (%m)")
  (erc-hide-list '("JOIN" "PART" "QUIT"))
  (erc-join-buffer 'bury)
  (erc-kill-buffer-on-part t)
  (erc-kill-queries-on-quit t)
  (erc-use-auth-source-for-nickserv-password t)
  (erc-kill-server-buffer-on-quit t)
  (erc-lurker-hide-list '("JOIN" "PART" "QUIT"))
  (erc-lurker-threshold-time 43200)
  (erc-prompt-for-nickserv-password nil)
  (erc-server-reconnect-attempts 5)
  (erc-server-reconnect-timeout 3)
  (erc-track-exclude-types '("JOIN" "MODE" "NICK" "PART" "QUIT"
                             "324" "329" "332" "333" "353" "477"))
  :config
  (add-to-list 'erc-modules 'notifications)
  (add-to-list 'erc-modules 'spelling)
  (erc-services-mode 1)
  (erc-update-modules))

(use-package erc-hl-nicks :after erc)
(use-package erc-image :after erc)

Ivy

I used helm before, but I find ivy faster and lighter.

Ivy is a generic completion mechanism for Emacs. While it operates similarly to other completion schemes such as icomplete-mode, Ivy aims to be more efficient, smaller, simpler, and smoother to use yet highly customizable.

Oleh Krehel

(use-package counsel
  :after ivy
  :delight
  :bind (("C-x C-d" . counsel-dired-jump)
         ("C-x C-h" . counsel-minibuffer-history)
         ("C-x C-l" . counsel-find-library)
         ("C-x C-r" . counsel-recentf)
         ("C-x C-u" . counsel-unicode-char)
         ("C-x C-v" . counsel-set-variable))
  :config (counsel-mode)
  :custom
  (counsel-rg-base-command "rg -S -M 150 --no-heading --line-number --color never %s"))

(use-package ivy
  :delight
  :after ivy-rich
  :bind (("C-x b" . ivy-switch-buffer)
         ("C-x B" . ivy-switch-buffer-other-window)
         ("M-H"   . ivy-resume)
         :map ivy-minibuffer-map
         ("<tab>" . ivy-alt-done)
         ("C-i" . ivy-partial-or-done)
         ("S-SPC" . nil)
         :map ivy-switch-buffer-map
         ("C-k" . ivy-switch-buffer-kill))
  :custom
  (ivy-case-fold-search-default t)
  (ivy-count-format "(%d/%d) ")
  (ivy-re-builders-alist '((t . ivy--regex-plus)))
  (ivy-use-virtual-buffers t)
  :config (ivy-mode))
(global-set-key (kbd "C-x j") 'counsel-git-grep)
(setq counsel-find-file-ignore-regexp (regexp-opt '("__pycache__" ".pyc" ".pytest_cache")))


(use-package ivy-pass
  :after ivy
  :commands ivy-pass)

(use-package ivy-rich
  :defer 0.1
  :preface
  (defun ivy-rich-branch-candidate (candidate)
    "Displays the branch candidate of the candidate for ivy-rich."
    (let ((candidate (expand-file-name candidate ivy--directory)))
      (if (or (not (file-exists-p candidate)) (file-remote-p candidate))
          ""
        (format "%s%s"
                (propertize
                 (replace-regexp-in-string abbreviated-home-dir "~/"
                                           (file-name-directory
                                            (directory-file-name candidate)))
                 'face 'font-lock-doc-face)
                (propertize
                 (file-name-nondirectory
                  (directory-file-name candidate))
                 'face 'success)))))

  (defun ivy-rich-compiling (candidate)
    "Displays compiling buffers of the candidate for ivy-rich."
    (let* ((candidate (expand-file-name candidate ivy--directory)))
      (if (or (not (file-exists-p candidate)) (file-remote-p candidate)
              (not (magit-git-repo-p candidate)))
          ""
        (if (my/projectile-compilation-buffers candidate)
            "compiling"
          ""))))

  (defun ivy-rich-file-group (candidate)
    "Displays the file group of the candidate for ivy-rich"
    (let ((candidate (expand-file-name candidate ivy--directory)))
      (if (or (not (file-exists-p candidate)) (file-remote-p candidate))
          ""
        (let* ((group-id (file-attribute-group-id (file-attributes candidate)))
               (group-function (if (fboundp #'group-name) #'group-name #'identity))
               (group-name (funcall group-function group-id)))
          (format "%s" group-name)))))

  (defun ivy-rich-file-modes (candidate)
    "Displays the file mode of the candidate for ivy-rich."
    (let ((candidate (expand-file-name candidate ivy--directory)))
      (if (or (not (file-exists-p candidate)) (file-remote-p candidate))
          ""
        (format "%s" (file-attribute-modes (file-attributes candidate))))))

  (defun ivy-rich-file-size (candidate)
    "Displays the file size of the candidate for ivy-rich."
    (let ((candidate (expand-file-name candidate ivy--directory)))
      (if (or (not (file-exists-p candidate)) (file-remote-p candidate))
          ""
        (let ((size (file-attribute-size (file-attributes candidate))))
          (cond
           ((> size 1000000) (format "%.1fM " (/ size 1000000.0)))
           ((> size 1000) (format "%.1fk " (/ size 1000.0)))
           (t (format "%d " size)))))))

  (defun ivy-rich-file-user (candidate)
    "Displays the file user of the candidate for ivy-rich."
    (let ((candidate (expand-file-name candidate ivy--directory)))
      (if (or (not (file-exists-p candidate)) (file-remote-p candidate))
          ""
        (let* ((user-id (file-attribute-user-id (file-attributes candidate)))
               (user-name (user-login-name user-id)))
          (format "%s" user-name)))))

  (defun ivy-rich-switch-buffer-icon (candidate)
    "Returns an icon for the candidate out of `all-the-icons'."
    (with-current-buffer
        (get-buffer candidate)
      (let ((icon (all-the-icons-icon-for-mode major-mode :height 0.9)))
        (if (symbolp icon)
            (all-the-icons-icon-for-mode 'fundamental-mode :height 0.9)
          icon))))
  :config
  (plist-put ivy-rich-display-transformers-list
             'counsel-find-file
             '(:columns
               ((ivy-rich-candidate               (:width 73))
                (ivy-rich-file-user               (:width 8 :face font-lock-doc-face))
                (ivy-rich-file-group              (:width 4 :face font-lock-doc-face))
                (ivy-rich-file-modes              (:width 11 :face font-lock-doc-face))
                (ivy-rich-file-size               (:width 7 :face font-lock-doc-face))
                (ivy-rich-file-last-modified-time (:width 30 :face font-lock-doc-face)))))
  (plist-put ivy-rich-display-transformers-list
             'counsel-projectile-switch-project
             '(:columns
               ((ivy-rich-branch-candidate        (:width 80))
                (ivy-rich-compiling))))
  (plist-put ivy-rich-display-transformers-list
             'ivy-switch-buffer
             '(:columns
               ((ivy-rich-switch-buffer-icon       (:width 2))
                (ivy-rich-candidate                (:width 40))
                (ivy-rich-switch-buffer-size       (:width 7))
                (ivy-rich-switch-buffer-indicators (:width 4 :face error :align right))
                (ivy-rich-switch-buffer-major-mode (:width 20 :face warning)))
               :predicate (lambda (cand) (get-buffer cand))))
  (ivy-rich-mode 1)
  (setq projectile-compile-project-cmd "cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=YES"))

(use-package all-the-icons-ivy
  :after (all-the-icons ivy)
  :custom (all-the-icons-ivy-buffer-commands '(ivy-switch-buffer-other-window))
  :config
  (add-to-list 'all-the-icons-ivy-file-commands 'counsel-dired-jump)
  (add-to-list 'all-the-icons-ivy-file-commands 'counsel-find-library)
  (all-the-icons-ivy-setup))

(use-package swiper
  :after ivy
  :bind (("C-s" . swiper)
         :map swiper-map
         ("M-%" . swiper-query-replace)))

Keybindings

F5 Refresh

Setting buffer refresh to F5 as usual in other programs.

(global-set-key [f5]
                #'(lambda () "Refresh the buffer from the disk (prompt of modified)."
                   (interactive)
                   (revert-buffer t (not (buffer-modified-p)) t)))

Font Size

(global-set-key (kbd "C-+") 'text-scale-increase)
(global-set-key (kbd "C--") 'text-scale-decrease)

Goto-line

Rebind goto-line to Meta+g rather than Meta+g+g

(global-set-key "\M-g" 'goto-line)

HHKB Super-Meta Switch

Check if HHKB connected by counting occurences in dmesg output. If exists swap super and meta keys.

(defun count-occurences (regex string)
  (recursive-count regex string 0))
(defun recursive-count (regex string start)
  (if (string-match regex string start)
      (+ 1 (recursive-count regex string (match-end 0)))
    0))
(setq dmesg-out
      (shell-command-to-string "/usr/bin/dmesg"))
(setq hhkb-times
      (count-occurences "HHKB" dmesg-out))
(if (> hhkb-times 0)
    (progn
      ;; (setq  x-meta-keysym 'meta
      ;;        x-super-keysym 'super)
      (message "HHKB connected on Linux.")))

MacOS Super-Meta Bind

Check if host system is running MacOS and bind super and meta if true.

(if (string-equal system-type "darwin")
    (progn
      (setq mac-command-modifier 'meta
            mac-option-modifier 'super)
      (message "MacOS detected.")))

Reload Config

(defun reload-init-file ()
  (interactive)
  (load-file "~/.config/emacs/init.el"))

(global-set-key (kbd "C-s-M-u") 'reload-init-file)

Linters

Flycheck lints warnings and errors directly within buffers.

(use-package flycheck
  :defer 2
  :delight
  :init (global-flycheck-mode)
  :custom
  (flycheck-display-errors-delay .3)
  (flycheck-checker-error-threshold 1000)
  (flycheck-pylintrc "~/.pylintrc")
  (flycheck-python-pylint-executable "/usr/bin/pylint")
  (flycheck-stylelintrc "~/.stylelintrc.json")
  :config
  (flycheck-add-mode 'javascript-eslint 'web-mode)
  (flycheck-add-mode 'typescript-tslint 'web-mode))

Lorem Ipsum

I could use try when I need to use lipsum, but since I use defer, the packet load attribute has no impact on emacs-init-time.

(use-package lorem-ipsum
  :bind (("C-c C-v l" . lorem-ipsum-insert-list)
         ("C-c C-v p" . lorem-ipsum-insert-paragraphs)
         ("C-c C-v s" . lorem-ipsum-insert-sentences)))

Navigation

Navigation is an important part of productivity. The next function is a more efficient way to go to the beginning of a line with move-beginning-of-line (C-a) and back-to-indentation (M-m).

FROM: http://emacsredux.com/blog/2013/05/22/smarter-navigation-to-the-beginning-of-a-line/

(defun my/smarter-move-beginning-of-line (arg)
  "Moves point back to indentation of beginning of line.

   Move point to the first non-whitespace character on this line.
   If point is already there, move to the beginning of the line.
   Effectively toggle between the first non-whitespace character and
   the beginning of the line.

   If ARG is not nil or 1, move forward ARG - 1 lines first. If
   point reaches the beginning or end of the buffer, stop there."
  (interactive "^p")
  (setq arg (or arg 1))

  ;; Move lines first
  (when (/= arg 1)
    (let ((line-move-visual nil))
      (forward-line (1- arg))))

  (let ((orig-point (point)))
    (back-to-indentation)
    (when (= orig-point (point))
      (move-beginning-of-line 1))))

(global-set-key (kbd "C-a") 'my/smarter-move-beginning-of-line)

(use-package imenu
  :ensure nil
  :bind ("C-r" . imenu))

Notmuch

Using `notmuch` as my email client. I followed this guide.

To use you’ll need to install both

sudo pacman -S notmuch
yay -S gmailieer-git
(use-package notmuch
  :ensure nil
  :commands (notmuch)
  :config
  (add-hook 'notmuch-hello-mode-hook
            (lambda () (display-line-numbers-mode 0))))

Configure your inboxes

(setq notmuch-saved-searches
   '((:name "inbox (omgitsaheadcrab)" :query "tag:inbox AND tag:omgitsaheadcrab" :sort-order newest-first)
     (:name "unread (omgitsaheadcrab)" :query "tag:unread AND tag:omgitsaheadcrab" :sort-order newest-first)
     (:name "sent (omgitsaheadcrab)" :query "tag:sent AND tag:omgitsaheadcrab" :sort-order newest-first)
     (:name "all mail (omgitsaheadcrab)" :query "tag:omgitsaheadcrab" :sort-order newest-first)
     (:name "inbox (t.backerdirks)" :query "tag:inbox AND tag:t.backerdirks" :sort-order newest-first)
     (:name "unread (t.backerdirks)" :query "tag:unread AND tag:t.backerdirks" :sort-order newest-first)
     (:name "sent (t.backerdirks)" :query "tag:sent AND tag:t.backerdirks" :sort-order newest-first)
     (:name "all mail (t.backerdirks)" :query "tag:t.backerdirks" :sort-order newest-first)
     (:name "drafts" :query "tag:draft" :sort-order newest-first)))

Configure senders to automatically choose the correct auth from `.authinfo.gpg`

(setq mail-specify-envelope-from t)
(setq message-sendmail-envelope-from 'header)
(setq mail-envelope-from 'header)
(setq notmuch-always-prompt-for-sender t)

;; Configure known SMTP servers. Emacs prompts for passwords and saves them in ~/.authinfo
(setq smtp-accounts          ;; Format: Sender Mail address - SMTP Server - Port - Username
      '(("omgitsaheadcrab@gmail.com" "smtp.gmail.com" 587 "omgitsaheadcrab@gmail.com")
        ("t.backerdirks@gmail.com" "smtp.gmail.com" 587 "t.backerdirks@gmail.com")
        ))

;; Set the SMTP Server according to the mail address we use for sending
(defun my/set-smtp-server-message-send-and-exit ()
  "Set SMTP server from list of multiple ones and send mail."
  (interactive)
  (message-remove-header "X-Message-SMTP-Method") ;; Remove. We always determine it by the From field
  (let ((sender
         (message-fetch-field "From")))
    (cl-loop for (addr server port usr) in smtp-accounts
          when (string-match addr sender)
          do (message-add-header (format "X-Message-SMTP-Method: smtp %s %d %s" server port usr)))
    (let ((xmess
           (message-fetch-field "X-Message-SMTP-Method")))
      (if xmess
          (progn
            (message (format "Sending message using '%s' with config '%s'" sender xmess))
            (message-send-and-exit))
        (error "Could not find SMTP Server for this Sender address: %s. You might want to correct it or add it to the SMTP Server list 'smtp-accounts'" sender)))))

;; Send emails via multiple servers
(defun my/local-notmuch-compose-mode ()
  "Keys."
  (local-set-key (kbd "C-c C-c")  'my/set-smtp-server-message-send-and-exit))

;; set in group mode hook
(add-hook 'notmuch-message-mode-hook 'my/local-notmuch-compose-mode)

Parentheses

Managing parentheses can be painful. One of the first things you want to do is to change the appearance of the highlight of the parentheses pairs.

(use-package faces
  :ensure nil
  :custom (show-paren-delay 0)
  :config
  (set-face-background 'show-paren-match "#262b36")
  (set-face-bold 'show-paren-match t)
  (set-face-foreground 'show-paren-match "#ffffff"))

rainbow-delimiters

rainbow-delimiters is a “rainbow parentheses”-like mode which highlights delimiters such as parentheses, brackets or braces according to their depth. Each successive level is highlighted in a different color. This makes it easy to spot matching delimiters, orient yourself in the code, and tell which statements are at a given depth.

Fanael Linithien

(use-package rainbow-delimiters
  :hook (prog-mode . rainbow-delimiters-mode))

smartparens

In my opinion, it is the most powerful package to deal with the parenthesis. Anyway, if you don’t like it, you can try taking a look at paredit or autopair.

(use-package smartparens
  :defer 1
  :delight
  :config
  (setq sp-show-pair-from-inside nil)
  (require 'smartparens-config)
  (smartparens-global-mode t)
  :diminish smartparens-mode)

Paste

This mode allows to paste whole buffers or parts of buffers to pastebin-like services. It supports more than one service and will failover if one service fails.

Elis Hirwing

(use-package webpaste :defer 1)

Same principle for images with imgbb. This package selects an image and upload it to imgbb, making sure to display the URL of the image in the minibuffer and place it in the kill ring.

(use-package imgbb :defer 2)

PDF

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.

Andras Politz

(use-package pdf-tools
  :defer 1
  :magic ("%PDF" . pdf-view-mode)
  :init (pdf-tools-install :no-query))

(use-package pdf-view
  :ensure nil
  :after pdf-tools
  :bind (:map pdf-view-mode-map
              ("C-s" . isearch-forward)
              ("d" . pdf-annot-delete)
              ("h" . pdf-annot-add-highlight-markup-annotation)
              ("t" . pdf-annot-add-text-annotation))
  :custom
  (pdf-view-display-size 'fit-page)
  (pdf-view-resize-factor 1.1)
  (pdf-view-use-unicode-ligther nil))

Point and Region

Increase region by semantic units. It tries to be smart about it and adapt to the structure of the current major mode.

(use-package expand-region
  :bind (("C->" . er/contract-region)
         ("C-." . er/expand-region)))

I find it useful to delete a line and a region with only C-w.

(defadvice kill-region (before slick-cut activate compile)
  "When called interactively with no active region, kill a single line instead."
  (interactive
   (if mark-active (list (region-beginning) (region-end))
     (list (line-beginning-position)
           (line-beginning-position 2)))))

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). For instance - finding project files has a portable implementation written in pure Emacs Lisp without the use of GNU find (but for performance sake an indexing mechanism backed by external commands exists as well).

Bozhidar Batsov

(use-package projectile
  :defer 1
  :preface
  (defun my/projectile-compilation-buffers (&optional project)
    "Get a list of a project's compilation buffers.
  If PROJECT is not specified the command acts on the current project."
    (let* ((project-root (or project (projectile-project-root)))
           (buffer-list (mapcar #'process-buffer compilation-in-progress))
           (all-buffers (cl-remove-if-not
                         (lambda (buffer)
                           (projectile-project-buffer-p buffer project-root))
                         buffer-list)))
      (if projectile-buffers-filter-function
          (funcall projectile-buffers-filter-function all-buffers)
        all-buffers)))
  :custom
  (projectile-cache-file (expand-file-name (format "%s/emacs/projectile.cache" xdg-cache)))
  (projectile-completion-system 'ivy)
  (projectile-enable-caching t)
  (projectile-keymap-prefix (kbd "C-c C-p"))
  (projectile-known-projects-file (expand-file-name (format "%s/emacs/projectile-bookmarks.eld" xdg-cache)))
  (projectile-mode-line '(:eval (projectile-project-name)))
  :config (projectile-global-mode))

(use-package counsel-projectile
  :after (counsel projectile)
  :config (counsel-projectile-mode 1))

Recent Files

Provides fast access to the recent files.

(use-package recentf
  :bind ("C-c r" . recentf-open-files)
  :init (recentf-mode)
  :custom
  (recentf-exclude (list "COMMIT_EDITMSG"
                         "~$"
                         "/scp:"
                         "/ssh:"
                         "/sudo:"
                         "/tmp/"))
  (recentf-max-menu-items 15)
  (recentf-max-saved-items 200)
  (recentf-save-file (expand-file-name (format "%s/emacs/recentf" xdg-cache)))
  :config (run-at-time nil (* 5 60) 'recentf-save-list))

Requests

Let’s follow the XDG base directory specification for the request and cookies files, in order to make ~/.emacs.d a cleaner place.

(use-package request
  :ensure nil
  :custom
  (request-storage-directory (expand-file-name (format "%s/emacs/request/" xdg-data))))

(use-package url-cookie
  :ensure nil
  :custom
  (url-cookie-file (expand-file-name (format "%s/emacs/url/cookies/" xdg-data))))

Smart Mx

Smart M-x for recent and most used command history.

(use-package smex
  :init (setq-default smex-history-length 10))

Spelling

Abbreviations

According to a list of misspelled words, abbrev auto-correct these words on the fly.

(use-package abbrev
  :ensure nil
  :delight
  :hook (text-mode . abbrev-mode)
  :custom (abbrev-file-name (expand-file-name (format "%s/emacs/abbrev_defs" xdg-data)))
  :config
  (if (file-exists-p abbrev-file-name)
      (quietly-read-abbrev-file)))

Fly Spell

For the other words that would not be in my list of abbreviations, flyspell enables spell checking on-the-fly in GNU Emacs.

(use-package flyspell
  :delight
  :hook ((markdown-mode org-mode text-mode) . flyspell-mode)
         (prog-mode . flyspell-prog-mode)
  :custom
  (flyspell-abbrev-p t)
  (flyspell-default-dictionary "en_US")
  (flyspell-issue-message-flag nil)
  (flyspell-issue-welcome-flag nil))

(use-package flyspell-correct-ivy
  :after (flyspell ivy)
  :init (setq flyspell-correct-interface #'flyspell-correct-ivy))

Spell Checker

No one is immune to spelling mistakes. So I like to check the spelling of the document once it has been written. To do this, I use hunspell, the modern spell checker.

NOTE: the reason I prefer hunspell to aspell is that according to the latest news, hunspell has made it possible to be more consistent on fly spells. However, most people still use aspell because it allows you to spot errors in camelCase, convenient for when you program. Personally, I just want to check the spelling in the comments and not in the whole document, so hunspell is perfect for me.

To use hunspell and the desired dictionaries on GNU Emacs, you must first install them (e.g. hunspell-en_US, hunspell-fr) with the package manager of your operating system.

(use-package ispell
  :defer 2
  :ensure-system-package (hunspell . "yay -S hunspell")
  :custom
  ;; to remove
  (ispell-local-dictionary "en_US")
  (ispell-local-dictionary-alist
   '(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)
     ("fr_BE" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "fr_BE") nil utf-8)))

  (ispell-dictionary "en_US")
  (ispell-dictionary-alist
   '(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)
     ("fr_BE" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "fr_BE") nil utf-8)))
  (ispell-program-name (executable-find "hunspell"))
  (ispell-really-hunspell t)
  (ispell-silently-savep t)
  :preface
  (defun my/switch-language ()
    "Switches between the English and French language."
    (interactive)
    (let* ((current-dictionary ispell-current-dictionary)
           (new-dictionary (if (string= current-dictionary "fr_BE") "en_US" "fr_BE")))
      (ispell-change-dictionary new-dictionary)
      (if (string= new-dictionary "fr_BE")
          (langtool-switch-default-language "fr")
        (langtool-switch-default-language "en"))

      ;;Clears all these old errors after switching to the new language
      (if (and (boundp 'flyspell-mode) flyspell-mode)
          (flyspell-mode 0)
        (flyspell-mode 1))

    (message "Dictionary switched from %s to %s" current-dictionary new-dictionary))))

Grammar Checker

LanguageTool is great for correcting your grammar. Combined with abbrev-mode and flyspell, you will have better documents. In order to be able to use it locally, download the desktop version and change the paths indicated below.

(use-package langtool
  :defer 2
  :delight
  :custom
  (langtool-default-language "en")
  (langtool-disabled-rules '("COMMA_PARENTHESIS_WHITESPACE"
                             "COPYRIGHT"
                             "DASH_RULE"
                             "EN_QUOTES"
                             "EN_UNPAIRED_BRACKETS"
                             "UPPERCASE_SENTENCE_START"
                             "WHITESPACE_RULE"))
  (langtool-java-classpath
    "/usr/share/languagetool:/usr/share/java/languagetool/*")
  (langtool-mother-tongue "en"))

Terminal

Colour

Enable more terminal colours.

(use-package eterm-256color
  :config
  (add-hook 'term-mode-hook #'eterm-256color-mode))

Use vterm

(use-package vterm
  :config
  (defun turn-off-chrome ()
    (hl-line-mode -1)
    (display-line-numbers-mode -1))
  :hook (vterm-mode . turn-off-chrome))

Hotkey

Setting hotkey for vterm in emacs - Super+T.

(use-package vterm-toggle
  :custom
  (vterm-toggle-fullscreen-p nil "Open a vterm in another window.")
  (vterm-toggle-scope 'project)
  :bind ("s-t" . #'vterm-toggle))

Useful Tweaks

Consistent Answers

Setting yes and no to y and n for brevity and consistency.

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

Delete Selection Mode

(delete-selection-mode 1)

IEdit

(use-package iedit
  :bind ("C-:" . iedit-mode))

Move Line

;; M-↑,↓ moves line, or marked region; prefix is how many lines.
(use-package move-text
  :config (move-text-default-bindings))

Persistent Scratch

Persistent the scratch buffer

(use-package persistent-scratch
  :preface
  (defun my-save-buffer ()
    "Save scratch and other buffer."
    (interactive)
    (let ((scratch-name "*scratch*"))
      (if (string-equal (buffer-name) scratch-name)
          (progn
            (message "Saving %s..." scratch-name)
            (persistent-scratch-save)
            (message "Wrote %s" scratch-name))
        (save-buffer))))
  :hook (persistent-scratch-setup-default)
  :bind (:map org-mode-map
              ("C-x C-s" . my-save-buffer))
  :config (persistent-scratch-setup-default))

Set persistent scratch as an org-mode buffer

(setq initial-major-mode (lambda ()
                           (persistent-scratch-mode)
                           (org-mode)))

Quote Lines

(defun quote-lines ()
  "Change current text block's lines to quoted lines with comma or other separator char.
  When there is a text selection, act on the selection, else, act on a text block separated by blank lines.

  For example,

   cat
   dog
   cow

  becomes

   \"cat\",
   \"dog\",
   \"cow\",

  or

   (cat)
   (dog)
   (cow)

  If the delimiter is any left bracket, the end delimiter is automatically the matching bracket.

  URL `http://ergoemacs.org/emacs/emacs_quote_lines.html'
  Version 2017-01-08"
  (interactive)
  (let* (
         $p1
         $p2
         ($quoteToUse
          (read-string
           "Quote to use:" "\"" nil
           '(
             ""
             "\""
             "'"
             "("
             "{"
             "["
             )))
         ($separator
          (read-string
           "line separator:" "," nil
           '(
             ""
             ","
             ";"
             )))
         ($beginQuote $quoteToUse)
         ($endQuote
          ;; if begin quote is a bracket, set end quote to the matching one. else, same as begin quote
          (let (($syntableValue (aref (syntax-table) (string-to-char $beginQuote))))
            (if (eq (car $syntableValue ) 4) ; ; syntax table, code 4 is open paren
                (char-to-string (cdr $syntableValue))
              $quoteToUse
              ))))
    (if (use-region-p)
        (progn
          (setq $p1 (region-beginning))
          (setq $p2 (region-end)))
      (progn
        (if (re-search-backward "\n[ \t]*\n" nil "NOERROR")
            (progn (re-search-forward "\n[ \t]*\n")
                   (setq $p1 (point)))
          (setq $p1 (point)))
        (re-search-forward "\n[ \t]*\n" nil "NOERROR")
        (skip-chars-backward " \t\n" )
        (setq $p2 (point))))
    (save-excursion
      (save-restriction
        (narrow-to-region $p1 $p2)
        (goto-char (point-min))
        (skip-chars-forward "\t ")
        (insert $beginQuote)
        (goto-char (point-max))
        (insert $endQuote)
        (goto-char (point-min))
        (while (re-search-forward "\n\\([\t ]*\\)" nil "NOERROR" )
          (replace-match
           (concat $endQuote $separator (concat "\n" (match-string 1)) $beginQuote) "FIXEDCASE" "LITERAL"))
        ;;
        ))))

Sudo Edit

(use-package sudo-edit
  :bind ("s-u" . sudo-edit))

UTF-8

UFT-8 Everywhere

(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)
(when (display-graphic-p)
  (setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)))

UTF-8 Uppercase Declaration

(define-coding-system-alias 'UTF-8 'utf-8)

Warning Bell Deactivation

Apparently there is a warning bell/beep and light - not seen it though..

(setq ring-bell-function 'ignore)

Version Control

It is quite common to work on Git repositories, so it is important to have a configuration that we like.

Magit is an interface to the version control system Git, implemented as an Emacs package. Magit aspires to be a complete Git porcelain. While we cannot (yet) claim that Magit wraps and improves upon each and every Git command, it is complete enough to allow even experienced Git users to perform almost all of their daily version control tasks directly from within Emacs. While many fine Git clients exist, only Magit and Git itself deserve to be called porcelains.

Jonas Bernoulli

(use-package git-commit
  :after magit
  :hook (git-commit-mode . my/git-commit-auto-fill-everywhere)
  :custom (git-commit-summary-max-length 50)
  :preface
  (defun my/git-commit-auto-fill-everywhere ()
    "Ensures that the commit body does not exceed 72 characters."
    (setq fill-column 72)
    (setq-local comment-auto-fill-only-comments nil)))

(use-package magit :defer 0.3)

(use-package smerge-mode
  :after hydra
  :hook (magit-diff-visit-file . (lambda ()
                                   (when smerge-mode
                                     (hydra-merge/body)))))

Finally, one last package that I like to use with Git to easily see the changes made by previous commits.

(use-package git-timemachine
  :defer 1
  :delight)

Weblorg

A Static HTML Generator for Emacs and Org-Mode.

(use-package weblorg
  :delight)

Whitespace

It is often annoying to see unnecessary blank spaces at the end of a line or file. Let’s get ride of them:

(use-package simple
  :ensure nil
  :hook (before-save . delete-trailing-whitespace))

hungry-delete

Deleting a whitespace character will delete all whitespace until the next non-whitespace character.

Nathaniel Flath

(use-package hungry-delete
  :defer 0.7
  :delight
  :config (global-hungry-delete-mode))

Windows

Don’t ask before killing a buffer. I know what I’m doing.

(global-set-key [remap kill-buffer] #'kill-this-buffer)

Most of the time, when I open a new window with C-x 2 or C-x 3 it is to switch directly to it and perform an action. By default, GNU Emacs does not give focus to the new window created. I have no idea why this is not the default behavior. But let’s refine these keys:

(use-package window
  :ensure nil
  :bind (("C-x 3" . hsplit-last-buffer)
         ("C-x 2" . vsplit-last-buffer))
  :preface
  (defun hsplit-last-buffer ()
    "Gives the focus to the last created horizontal window."
    (interactive)
    (split-window-horizontally)
    (other-window 1))

  (defun vsplit-last-buffer ()
    "Gives the focus to the last created vertical window."
    (interactive)
    (split-window-vertically)
    (other-window 1)))

switch-window

Displays an overlay in each window showing a unique key, then asks the user where to move in the window.

Most people use ace-window, but I prefer switch-window because I find this package more ergonomic by using the fact of displaying the buffer number by hiding its contents.

(use-package switch-window
  :bind (("C-x o" . switch-window)
         ("C-x w" . switch-window-then-swap-buffer)))

windmove

Allows you to move from one window to another with something more natural than cycling through C-x o (other-window).

(use-package windmove
  :bind (("C-c h" . windmove-left)
         ("C-c j" . windmove-down)
         ("C-c k" . windmove-up)
         ("C-c l" . windmove-right)))

winner

I often undo’s and redo’s with window configurations.

Winner mode is a global minor mode that records the changes in the window configuration (i.e. how the frames are partitioned into windows) so that the changes can be “undone” using the command winner-undo. By default, this one is bound to the key sequence ctrl-c left. If you change your mind (while undoing), you can press ctrl-c right (calling winner-redo).

Ivar Rummelhoff

(use-package winner
  :defer 2
  :config (winner-mode 1))

Word Wrap

I like to have lines of the same length in text mode.

(use-package simple
  :ensure nil
  :delight (auto-fill-function)
  :bind ("C-x p" . pop-to-mark-command)
  :hook (text-mode . turn-on-auto-fill)
  :custom (set-mark-command-repeat-pop t))

YASnippet

YASnippet is a template system for Emacs. It allows you to type an abbreviation and automatically expand it into function templates.

João Távora

(use-package yasnippet-snippets
  :after yasnippet
  :config (yasnippet-snippets-initialize))

(use-package yasnippet
  :delight yas-minor-mode " υ"
  :hook (yas-minor-mode . my/disable-yas-if-no-snippets)
  :config (yas-global-mode)
  :preface
  (defun my/disable-yas-if-no-snippets ()
    (when (and yas-minor-mode (null (yas--get-snippet-tables)))
      (yas-minor-mode -1))))

(use-package ivy-yasnippet :after yasnippet)
(use-package react-snippets :after yasnippet)

Zetteldeft

Extend the deft package and turn it into a (very very) basic Zettelkasten note-taking system. Requires Avy for text movement.

(use-package avy)
(use-package zetteldeft)
(zetteldeft-set-classic-keybindings)

Org-Mode

One of my favorite modes in GNU Emacs. I mainly use it to organize my life, take notes and make my presentations, but you can do lots of things with it. org-mode it’s like the sky, without limits.

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

Carsten Dominik

(use-package org
  :ensure org-contrib
  :delight "Θ "
  :bind ("C-c i" . org-insert-structure-template)
  :preface
  (defvar my/org-default-blog-posts-dir "~/Documents/org/site/posts" "Default directory for blog posts")
  (defun my/org-compare-times (clocked estimated)
    "Gets the ratio between the timed time and the estimated time."
    (if (and (> (length clocked) 0) estimated)
        (format "%.2f"
                (/ (* 1.0 (org-hh:mm-string-to-minutes clocked))
                   (org-hh:mm-string-to-minutes estimated)))
      ""))
  (defvar my/delete-frame-after-capture 0 "Whether to delete the last frame after the current capture")
  (defun my/delete-frame-if-neccessary (&rest r)
    (cond
     ((= my/delete-frame-after-capture 0) nil)
     (t
      (setq my/delete-frame-after-capture 0)
      (delete-frame))))
  (defun my/org-archive-done-tasks ()
    "Archives finished or cancelled tasks."
    (interactive)
    (org-map-entries
     (lambda ()
       (org-archive-subtree)
       (setq org-map-continue-from (outline-previous-heading)))
     "TODO=\"DONE\"|TODO=\"CANCELLED\"" (if (org-before-first-heading-p) 'file 'tree)))

  (defun my/org-jump ()
    "Jumps to a specific task."
    (interactive)
    (let ((current-prefix-arg '(4)))
      (call-interactively 'org-refile)))

  (defun my/org-use-speed-commands-for-headings-and-lists ()
    "Activates speed commands on list items too."
    (or (and (looking-at org-outline-regexp) (looking-back "^\**"))
        (save-excursion (and (looking-at (org-item-re)) (looking-back "^[ \t]*")))))
  (defmacro ignore-args (fnc)
    "Returns function that ignores its arguments and invokes FNC."
    `(lambda (&rest _rest)
       (funcall ,fnc)))
  :hook ((after-save . my/config-tangle)
         ;(auto-save . org-save-all-org-buffers)
         (org-mode . org-indent-mode))
  :custom
  (org-archive-location "~/Documents/org/archives/%s::")
  (org-blank-before-new-entry '((heading . t)
                                (plain-list-item . t)))
  (org-cycle-include-plain-lists 'integrate)
  (org-ditaa-jar-path "~/.local/lib/ditaa0_9.jar")
  (org-expiry-inactive-timestamps t)
  (org-export-backends '(ascii beamer html icalendar latex man md org texinfo))
  (org-log-done 'time)
  (org-log-into-drawer "LOGBOOK")
  (org-modules '(org-crypt
                 org-habit
                 ol-info
                 ol-irc
                 org-mouse
                 org-protocol
                 org-tempo))
  (org-refile-allow-creating-parent-nodes 'confirm)
  (org-refile-use-cache nil)
  (org-refile-use-outline-path nil)
  (org-refile-targets '((org-agenda-files . (:maxlevel . 6))))
  (org-startup-folded nil)
  (org-startup-with-inline-images t)
  (org-tag-alist '(("@coding" . ?c)
                   ("@computer" . ?l)
                   ("@errands" . ?e)
                   ("@home" . ?h)
                   ("@phone" . ?p)
                   ("@reading" . ?r)
                   ("@school" . ?s)
                   ("@work" . ?b)
                   ("@writing" . ?w)
                   ("crypt" . ?C)
                   ("fuzzy" . ?0)
                   ("highenergy" . ?1)))
  (org-tags-exclude-from-inheritance '("crypt" "project"))
  (org-todo-keywords '((sequence "TODO(t)"
                                 "STARTED(s)"
                                 "WAITING(w@/!)"
                                 "SOMEDAY(.)" "|" "DONE(d!)" "CANCELLED(c@)")
                       (sequence "DONE(d)")))
  (org-use-effective-time t)
  (org-use-speed-commands 'my/org-use-speed-commands-for-headings-and-lists)
  (org-yank-adjusted-subtrees t)
  :config
  (add-to-list 'org-global-properties '("Effort_ALL". "0:05 0:15 0:30 1:00 2:00 3:00 4:00"))
  (add-to-list 'org-speed-commands '("!" my/org-clock-in-and-track))
  (add-to-list 'org-speed-commands '("$" call-interactively 'org-archive-subtree))
  (add-to-list 'org-speed-commands '("d" my/org-move-line-to-destination))
  (add-to-list 'org-speed-commands '("i" call-interactively 'org-clock-in))
  (add-to-list 'org-speed-commands '("o" call-interactively 'org-clock-out))
  (add-to-list 'org-speed-commands '("s" call-interactively 'org-schedule))
  (add-to-list 'org-speed-commands '("x" org-todo "DONE"))
  (add-to-list 'org-speed-commands '("y" org-todo-yesterday "DONE"))
  (advice-add 'org-deadline :after (ignore-args #'org-save-all-org-buffers))
  (advice-add 'org-schedule :after (ignore-args #'org-save-all-org-buffers))
  (advice-add 'org-store-log-note :after (ignore-args #'org-save-all-org-buffers))
  (advice-add 'org-todo :after (ignore-args #'org-save-all-org-buffers))
  (advice-add 'org-capture-finalize :after 'my/delete-frame-if-neccessary)
  (advice-add 'org-capture-kill :after 'my/delete-frame-if-neccessary)
  (advice-add 'org-capture-refile :after 'my/delete-frame-if-neccessary)
  (org-clock-persistence-insinuate)
  (org-load-modules-maybe t))

If like me, you’re tired of manually updating your tables of contents, toc-org will maintain a table of contents at the first heading that has a :TOC: tag.

(use-package toc-org
  :after org
  :hook (org-mode . toc-org-enable))

For a cleaner online mode.

(use-package org-indent :ensure nil :after org :delight)

Agenda

Nowadays, it is crucial to be organized. Even more than before. That is why it is important to take the time to make a configuration that is simple to use and that makes your life easier with an irreproachable organization.

org-agenda allows me to be organized with daily tasks. As a result, I can use my time to the fullest.

I put my org files in Syncthing in order to be able to check my agenda and update it from several computers and smartphones.

(use-package org-agenda
  :ensure nil
  :bind (:map org-agenda-mode-map
              ("X" . my/org-agenda-mark-done-and-add-followup)
              ("x" . my/org-agenda-done))
  :preface
  (defun my/org-agenda-done (&optional arg)
    "Mark current TODO as done.
  This changes the line at point, all other lines in the agenda referring to
  the same tree node, and the headline of the tree node in the Org-mode file."
    (interactive "P")
    (org-agenda-todo "DONE"))

  (defun my/org-agenda-mark-done-and-add-followup ()
    "Mark the current TODO as done and add another task after it.
   Creates it at the same level as the previous task, so it's better to use
   this with to-do items than with projects or headings."
    (interactive)
    (org-agenda-todo "DONE")
    (org-agenda-switch-to)
    (org-capture 0 "t"))
  :custom
  (org-directory "~/Documents/org")
  (org-agenda-dim-blocked-tasks t)
  (org-agenda-files (directory-files-recursively org-directory "\\.org$"))
  (org-agenda-inhibit-startup t)
  (org-agenda-show-log t)
  (org-agenda-skip-deadline-if-done t)
  (org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled)
  (org-agenda-skip-scheduled-if-done t)
  (org-agenda-span 2)
  (org-agenda-start-on-weekday 6)
  (org-agenda-sticky nil)
  (org-agenda-tags-column -100)
  (org-agenda-time-grid '((daily today require-timed)))
  (org-agenda-use-tag-inheritance t)
  (org-columns-default-format "%14SCHEDULED %Effort{:} %1PRIORITY %TODO %50ITEM %TAGS")
  (org-default-notes-file (format "%s/%s" org-directory "inbox.org"))
  (org-enforce-todo-dependencies t)
  (org-habit-completed-glyph ?✓)
  (org-habit-graph-column 80)
  (org-habit-show-habits-only-for-today nil)
  (org-habit-today-glyph ?‖)
  (org-track-ordered-property-with-tag t))

Bullets

Prettier bullets in org-mode.

(use-package org-bullets
  :hook (org-mode . org-bullets-mode)
  :custom
  (org-bullets-bullet-list '("" "" "")))

Capture

org-capture templates saves you a lot of time when adding new entries. I use it to quickly record tasks, ledger entries, notes and other semi-structured information.

(use-package org-capture
  :ensure nil
  :after org
  :preface
  (defvar my/org-basic-task-template "* TODO %^{Task}
:PROPERTIES:
:Effort: %^{effort|1:00|0:05|0:15|0:30|2:00|4:00}
:END:
Captured %<%Y-%m-%d %H:%M>" "Template for basic task.")

  (defvar my/org-contacts-template "* %(org-contacts-template-name)
:PROPERTIES:
:ADDRESS: %^{289 Cleveland St. Brooklyn, 11206 NY, USA}
:BIRTHDAY: %^{yyyy-mm-dd}
:EMAIL: %(org-contacts-template-email)
:NOTE: %^{NOTE}
:END:" "Template for org-contacts.")

  (defvar my/org-blog-post-template "* TODO %^{Post Title}
#+DATE: <%<%Y-%m-%d>>
#+OPTIONS: toc:nil num:nil
#+FILETAGS: %^{:blogging:}" "Template for org-blog-posts.")

  (defun transform-square-brackets-to-round-ones(string-to-transform)
    "Transforms [ into ( and ] into ), other chars left unchanged."
    (concat
     (mapcar (lambda (c) (if (equal c ?\[) ?\( (if (equal c ?\]) ?\) c))) string-to-transform))
    )
  :custom
  (org-capture-templates
   `(("B" "Book" checkitem (file+headline "~/Documents/org/other/books.org" "Books")
      "- [ ] %^{Book}"
      :immediate-finish t)

     ("w" "Work" entry (file+headline "~/Documents/org/agenda/work.org" "Tasks"),
      my/org-basic-task-template
      :empty-lines 1)

     ("t" "Task" entry (file+headline "~/Documents/org/agenda/organizer.org" "Tasks"),
      my/org-basic-task-template
      :empty-lines 1)

     ("i" "Inbox" entry (file+headline "~/Documents/org/inbox.org" "Inbox"),
      my/org-basic-task-template
      :empty-lines 1)

     ("p" "Post" entry (file+headline "~/Documents/org/inbox.org" "Inbox"),
      my/org-blog-post-template
      :empty-lines 1)

     ("q" "Protocol Quote" entry (file+headline "~/Documents/org/inbox.org" "Inbox")
      "* TODO %^{Title}\nSource: [[%:link][%(transform-square-brackets-to-round-ones \"%:description\")]]\n#+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n%? %(progn (setq my/delete-frame-after-capture 1) \"\")\nCaptured On: %U"
      :empty-lines 1)

     ("l" "Protocol Link" entry (file+headline "~/Documents/org/inbox.org" "Inbox")
      "* TODO %? [[%:link][%(transform-square-brackets-to-round-ones \"%:description\")]] %(progn (setq my/delete-frame-after-capture 1) \"\")\nCaptured On: %U"
      :empty-lines 1)
     )))

Clock

Being organized is one thing, but being optimal is another. org-clock allows you to estimate your tasks and time them. This is useful, since with experience, you can have a better estimate of the time that needs to be given to each task.

(use-package org-clock
  :ensure nil
  :after org
  :preface
  (defun my/org-mode-ask-effort ()
    "Ask for an effort estimate when clocking in."
    (unless (org-entry-get (point) "Effort")
      (let ((effort
             (completing-read
              "Effort: "
              (org-entry-get-multivalued-property (point) "Effort"))))
        (unless (equal effort "")
          (org-set-property "Effort" effort)))))
  :hook (org-clock-in-prepare-hook . my/org-mode-ask-effort)
  :custom
  (org-clock-clocktable-default-properties
   '(:block day :maxlevel 2 :scope agenda :link t :compact t :formula %
            :step day :fileskip0 t :stepskip0 t :narrow 80
            :properties ("Effort" "CLOCKSUM" "CLOCKSUM_T" "TODO")))
  (org-clock-continuously nil)
  (org-clock-in-switch-to-state "STARTED")
  (org-clock-out-remove-zero-time-clocks t)
  (org-clock-persist t)
  (org-clock-persist-file (expand-file-name (format "%s/emacs/org-clock-save.el" xdg-cache)))
  (org-clock-persist-query-resume nil)
  (org-clock-report-include-clocking-task t)
  (org-show-notification-handler (lambda (msg) (alert msg))))

Being a person with a very low ability to concentrate, I use the pomodoro method to be able to concentrate throughout the day and have deep focus sessions almost all the time.

Personally, I do sessions of:

  • 25 minutes of concentrated work;
  • 5 minutes break.

Finally, I take a 30-minute break every two hours.

To allow this, I use org-pomodoro making sure I get that in my status bar:

(use-package org-pomodoro
  :defer 0.5
  :custom
  (alert-user-configuration (quote ((((:category . "org-pomodoro")) libnotify nil))))
  (org-pomodoro-audio-player "/usr/bin/mpv")
  (org-pomodoro-finished-sound "~/Audio/pomodoro_finished.mp3")
  (org-pomodoro-format " %s")
  (org-pomodoro-killed-sound "~/Audio/pomodoro_killed.mp3")
  (org-pomodoro-long-break-sound "~/Audio/pomodoro_long.mp3")
  (org-pomodoro-overtime-sound "~/Audio/pomodoro_overtime.mp3")
  (org-pomodoro-short-break-sound "~/Audio/pomodoro_short.mp3")
  (org-pomodoro-start-sound "~/Audio/pomodoro_start.mp3")
  (org-pomodoro-start-sound-p t))

Contacts

The best solution to maintain your contacts. I tend to use org-contacts to remember their birthdays, so I can be the first to wish them that. Be careful that to install it, this one is available with org-contrib.

(use-package org-contacts
  :ensure nil
  :after org
  :custom (org-contacts-files '("~/Documents/org/agenda/contacts.org")))

Customization

Let’s change the foreground and the weight of each keyword.

(use-package org-faces
  :ensure nil
  :after org
  :custom
  (org-todo-keyword-faces
   '(("DONE" . (:foreground "cyan" :weight bold))
     ("SOMEDAY" . (:foreground "gray" :weight bold))
     ("TODO" . (:foreground "green" :weight bold))
     ("WAITING" . (:foreground "red" :weight bold)))))

Encryption / Decryption

To be able to enable encryption and decryption of .gpg files with org-mode, we will need to install gnupg2.

Once this is done, we simply configure org-crypt to accept our public key identifier to allow asymmetric encryption.

NOTE: you need to modify the org-crypt-key variable to replace my key identifier, by yours (or nil to allow symmetric encryption).

 (use-package org-crypt
   :ensure nil
	:after org
   :init (org-crypt-use-before-save-magic)
   :custom (org-crypt-key "E9AADC36E94A672D1A07D49B208FCDBB98190562"))

Journal

Recently, I started writing a journal about my daily life as I read that journals improve mental clarity, help solve problems, improve overall focus, insight and understanding, track the overall development and facilitate personal growth.

(use-package org-journal
  :after org
  :bind (("C-c T" . org-journal-new-entry)
         ("C-c Y" . journal-file-yesterday))
  :preface
  (defun get-journal-file-yesterday ()
    "Gets filename for yesterday's journal entry."
    (let* ((yesterday (time-subtract (current-time) (days-to-time 1)))
           (daily-name (format-time-string "%Y%m%d" yesterday)))
      (expand-file-name (concat org-journal-dir daily-name))))

  (defun journal-file-yesterday ()
    "Creates and load a file based on yesterday's date."
    (interactive)
    (find-file (get-journal-file-yesterday)))
  :custom
  (org-journal-date-format "%e %b %Y (%A)")
  (org-journal-dir (format "~/Documents/org/journal/" (format-time-string "%Y")))
  (org-journal-enable-encryption t)
  (org-journal-file-format "%Y%m%d")
  (org-journal-time-format ""))

Languages

With that, I can compile many languages with org-mode.

(use-package ob-C :ensure nil :after org)
(use-package ob-css :ensure nil :after org)
(use-package ob-ditaa :ensure nil :after org)
(use-package ob-dot :ensure nil :after org)
(use-package ob-emacs-lisp :ensure nil :after org)
(use-package ob-gnuplot :ensure nil :after org)
(use-package ob-java :ensure nil :after org)
(use-package ob-js :ensure nil :after org)

(use-package ob-latex
  :ensure nil
  :after org
  :custom (org-latex-compiler "xelatex"))

(use-package ob-ledger :ensure nil :after org)
(use-package ob-makefile :ensure nil :after org)
(use-package ob-org :ensure nil :after org)

(use-package ob-plantuml
  :ensure nil
  :after org
  :custom (org-plantuml-jar-path (expand-file-name (format "%s/plantuml.jar" xdg-lib))))

(use-package ob-python :ensure nil :after org)
(use-package ob-ruby :ensure nil :after org)
(use-package ob-shell :ensure nil :after org)
(use-package ob-sql :ensure nil :after org)

Refile to File

Add ability to refile org subtrees to a new file.

Functions

Custom functions to enable refiling org subtrees to a new file.

FROM: http://www.howardism.org/Technical/Emacs/getting-even-more-boxes-done.html

(defun my/org-subtree-metadata ()
  "Return a list of key aspects of an org-subtree. Includes the
         following: header text, body contents, list of tags, region list
         of the start and end of the subtree."
  (save-excursion
    ;; Jump to the parent header if not already on a header
    (when (not (org-at-heading-p))
      (org-previous-visible-heading 1))

    (let* ((context (org-element-context))
           (attrs   (second context))
           (props   (org-entry-properties)))

      (list :region     (list (plist-get attrs :begin) (plist-get attrs :end))
            :header     (plist-get attrs :title)
            :tags       (my/org-get-subtree-tags props)
            :properties (my/org-get-subtree-properties attrs)
            :body       (my/org-get-subtree-content attrs)))))

(defun my/org-get-subtree-tags (&optional props)
  "Given the properties, PROPS, from a call to
         `org-entry-properties', return a list of tags."
  (unless props
    (setq props (org-entry-properties)))
  (let ((tag-label (if my/org-get-subtree-tags-inherited "ALLTAGS" "TAGS")))
    (-some->> props
      (assoc tag-label)
      cdr
      substring-no-properties
      (s-split ":")
      (--filter (not (equalp "" it))))))

(defvar my/org-get-subtree-tags-inherited t
  "Returns a subtree's tags, and all tags inherited (from tags
           specified in parents headlines or on the file itself). Defaults
           to true.")

(defun my/org-get-subtree-properties (attributes)
  "Return a list of tuples of a subtrees properties where the keys are strings."

  (defun my/symbol-upcase? (sym)
    (let ((case-fold-search nil))
      (string-match-p "^:[A-Z]+$" (symbol-name sym))))

  (defun my/convert-tuple (tup)
    (let ((key (first tup))
          (val (second tup)))
      (list (substring (symbol-name key) 1) val)))

  (->> attributes
       (-partition 2)                         ; Convert plist to list of tuples
       (--filter (my/symbol-upcase? (first it))) ; Remove lowercase tuples
       (-map 'my/convert-tuple)))

(defun my/org-get-subtree-content (attributes)
  "Return the contents of the current subtree as a string."
  (let ((header-components '(clock diary-sexp drawer headline inlinetask
                                   node-property planning property-drawer section)))

    (goto-char (plist-get attributes :contents-begin))

    ;; Walk down past the properties, etc.
    (while
        (let* ((cntx (org-element-context))
               (elem (first cntx))
               (props (second cntx)))
          (when (member elem header-components)
            (goto-char (plist-get props :end)))))

    ;; At this point, we are at the beginning of what we consider
    ;; the contents of the subtree, so we can return part of the buffer:
    (buffer-substring-no-properties (point) (org-end-of-subtree))))

(defun my/org-refile-subtree-to-file (dir)
  "Archive the org-mode subtree and create an entry in the
   directory folder specified by DIR. It attempts to move as many of
   the subtree's properties and other features to the new file."
  (interactive "DDestination: ")
  (let* ((props      (my/org-subtree-metadata))
         (head       (plist-get props :header))
         (body       (plist-get props :body))
         (tags       (plist-get props :tags))
         (properties (plist-get props :properties))
         (area       (plist-get props :region))
         (filename   (my/org-filename-from-title head))
         (filepath   (format "%s/%s.org" dir filename)))
    (apply #'delete-region area)
    (my/org-create-org-file filepath head body tags properties)))


(defun my/org-create-org-file (filepath header body tags properties)
  "Create a new Org file by FILEPATH. The contents of the file is
    pre-populated with the HEADER, BODY and any associated TAGS."
  (find-file-other-window filepath)
  (my/org-set-file-property "TITLE" header t)
  (when tags
    (my/org-set-file-property "FILETAGS" (s-join " " tags)))

  ;; Insert any drawer properties as #+PROPERTY entries:
  (when properties
    (goto-char (point-min))
    (or (re-search-forward "^\s*$" nil t) (point-max))
    (--map (insert (format "#+PROPERTY: %s %s" (first it) (second it))) properties))

  ;; My auto-insert often adds an initial headline for a subtree, and in this
  ;; case, I don't want that... Yeah, this isn't really globally applicable,
  ;; but it shouldn't cause a problem for others.
  (when (re-search-forward "^\\* [0-9]$" nil t)
    (replace-match ""))

  (delete-blank-lines)
  (goto-char (point-max))
  (insert "\n")
  (insert body))

(defun my/org-filename-from-title (title)
  "Creates a useful filename based on a header string, TITLE.
  For instance, given the string:    What's all this then?
       This function will return:    whats-all-this-then"
  (let* ((no-letters (rx (one-or-more (not alphanumeric))))
         (init-try (->> title
                        downcase
                        (replace-regexp-in-string "'" "")
                        (replace-regexp-in-string no-letters "-"))))
    (string-trim init-try "-+" "-+")))


(defun my/org-set-file-property (key value &optional spot)
  "Make sure file contains a top-level, file-wide property.
 KEY is something like `TITLE' or `FILETAGS'. This function makes
 sure that the property contains the contents of VALUE, and if the
 file doesn't have the property, it is inserted at either SPOT, or
 if nil,the top of the file."
  (save-excursion
    (goto-char (point-min))
    (let ((case-fold-search t))
      (if (re-search-forward (format "^#\\+%s:\s*\\(.*\\)" key) nil t)
          (replace-match value nil nil nil 1)

        (cond
         ;; if SPOT is a number, go to it:
         ((numberp spot) (goto-char spot))
         ;; If SPOT is not given, jump to first blank line:
         ((null spot) (progn (goto-char (point-min))
                             (re-search-forward "^\s*$" nil t)))
         (t (goto-char (point-min))))

        (insert (format "#+%s: %s\n" (upcase key) value))))))

Destinations

Add blog post.

(defun my/org-refile-to-blog-post-dir ()
    "Move the current subtree to a file in the blog `posts' directory."
    (interactive)
    (my/org-refile-subtree-to-file my/org-default-blog-posts-dir))