github soundcloud
init

This is my Emacs configuration following the literate programming paradigm. A concept introduced by Donald Knuth where prose and source code is intertwined.

Emacs is my secret weapon for increasing the bandwith between brain and machine.

HTML version
const.no/init (courtesy of ox-hugo and hugo)
Git repo
gitlab.com/constno/emacs.d

About

Open Emacs. Open this file. Run M-x org-babel-tangle to tangle the source blocks of this file to ~/.emacs.d/init.el. Delete ~/.emacs if it exists. Restart Emacs and rejoice.

  • straight.el downloads packages
  • use-package configures and loads packages
  • general.el sets keybindings
  • evil emulates Vim perfectly

I prefer Vim, so naturally I use Emacs. My Emacs config is inspired by Spacemacs and Doom’s mnemonic nested menu system. To get started creating your own, I recommend these resources:

Many clever ideas in this configuration have been discovered by using GitHub search. Try searching for <package-name> in commits or use-package <package-name> in code. It’s also a great way to learn new libraries in other programming languages.

You can also search by file extension:

occur extension:el extension:org

Research your own experience; absorb what is useful, reject what is useless and add what is essentially your own.

Remember:

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Complex is better than complicated.

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.

This config is based around use-package for package usage and general for keybindings. They work together. If I use the :general keyword it means that the package will only load when I press the keybinding (or run the bound command).

What I cannot create, I do not understand.

If you wonder why Lisp is worth learning, take a look at The Nature of Lisp and The Emacs problem.

Some resources for learning Lisp:

Favourite dotfiles

Emacs, Vim or VS Code?

I used to think living in the terminal was the highest form of effective computing. It turns out I was wrong. Once I started learning about Emacs and literate programming in Org mode, things just started to fall into place. Vim was not the answer. Doing everything in the shell was not the answer. Now I kinda feel sorry for myself for not realizing earlier. Emacs has a bad reputation for having unergonomic default keybindings, so I stayed away. Vim must be the best, because you never leave the home row, right? Well, yeah, that’s why this config uses evil-mode, which makes Emacs the best Vi.

You should try Emacs if:

  • You get easily demotivated by being ineffective
  • You feel like the tools you are using hold you back from thinking clearly and that it may hide your true potential
  • You thrive while using tools with superior cognitive ergonomics
  • You feel a sense of disorientation and friction while using other tools and have an inkling feeling they are holding you back somehow
  • You would like better documentation and feature discoverability (keybindings etc)
  • You would like to get more out of your programming skills (org-babel will let you empower your writing environment with programming languages)
  • You are looking for the perfect digital notebook (org-mode is the answer)

Challenge your cognitive dissonance and take a good look at what Emacs is capable of.

For a philosophical view:

Have fun!

Heroes using Emacs

I’ve noticed that many of the most productive and creative programmers use Emacs. Some of them are:

  • Raymond Hettinger
  • Brandon Rhodes
  • Rich Hickey
  • Guido van Rossum
  • Linus Torvalds
  • Donald Knuth
  • Peter Norvig
  • Gergely Nagy

More examples at Wenshan’s Blog.

A good old appeal to authority doesn’t hurt anyone.

Startup optimizations

Disabling GC (by setting gc-cons-threshold to a very large value) during startup is said to improve startup time by reducing the number of GC runs.

;; -*- lexical-binding: t; -*-

;;; Temporarily reduce garbage collection during startup. Inspect `gcs-done'.
(defun ambrevar/reset-gc-cons-threshold ()
  (setq gc-cons-threshold (car (get 'gc-cons-threshold 'standard-value))))

(setq gc-cons-threshold (* 64 1024 1024))

(add-hook 'after-init-hook #'ambrevar/reset-gc-cons-threshold)

;;; Temporarily disable the file name handler.
(setq default-file-name-handler-alist file-name-handler-alist)

(setq file-name-handler-alist nil)

(defun ambrevar/reset-file-name-handler-alist ()
  (setq file-name-handler-alist
        (append default-file-name-handler-alist
                file-name-handler-alist))
  (cl-delete-duplicates file-name-handler-alist :test 'equal))

(add-hook 'after-init-hook #'ambrevar/reset-file-name-handler-alist)

(setq load-prefer-newer t)

Base settings

Welcome to user-init-file which will be tangled to user-emacs-directory.

Don’t load ~/.emacs.

(setq inhibit-default-init 't)
;; (setq browse-url-browser-function 'browse-url-firefox)

Enable folding in programming modes:

(add-hook 'prog-mode-hook #'hs-minor-mode)

Disable GUI components

(menu-bar-mode -1)
(scroll-bar-mode -1)
(tool-bar-mode -1)
(blink-cursor-mode 0)
(setq inhibit-startup-screen t)
(setq inhibit-startup-message t)
(setq inhibit-startup-echo-area-message t)
(setq initial-scratch-message nil)
(setq display-line-numbers-type nil)

(defun display-startup-echo-area-message ()
  (message ""))

Always answer yes or no with “y” or “n”.

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

Line fill

Some quality of life things:

(add-hook 'text-mode-hook 'turn-on-auto-fill)
(setq adaptive-fill-mode t)
(setq scroll-preserve-screen-position t)

Backups

Put backups somewhere else. Don’t create lockfiles.

(with-eval-after-load 'tramp
  (setq tramp-default-method "ssh"))

Some beta version of Emacs on Mac started defaulting to /, so I set the default directory manually.

(setq default-directory (concat (getenv "HOME") "/"))

(global-hl-line-mode 1)

(set-language-environment "UTF-8")
(set-default-coding-systems 'utf-8)
(setq-default indent-tabs-mode nil)
(winner-mode 1)
(put 'narrow-to-region 'disabled nil)

(setq scroll-margin 10
   scroll-step 1
   next-line-add-newlines nil
   scroll-conservatively 10000
   scroll-preserve-screen-position 1)

(setq mouse-wheel-follow-mouse 't)
(setq mouse-wheel-scroll-amount '(1 ((shift) . 1)))

Here is a function for reloading init.el.

(defun reload-init-file ()
  (interactive)
  (load-file user-init-file))

Mac

(when (string= system-type "darwin")
  (setq dired-use-ls-dired nil))

;; Both command keys are 'Super'
(setq mac-right-command-modifier 'super)
(setq mac-command-modifier 'super)

;; Option or Alt is naturally 'Meta'
(setq mac-option-modifier 'meta)

;; Right Alt (option) can be used to enter symbols like em dashes '—' and euros '€' and stuff.
(setq mac-right-option-modifier 'nil)

Straight bootstrap

The first time this config is evaluated, straight.el will be downloaded. Succeeding that, it will download all the packages defined by use-package automatically. The packages will typically be downloaded from GitHub (contrary to package.el that downloads packages from ELPA etc). You may create a lockfile by running straight-freeze-versions which creates ~/.emacs.d/straight/versions/default.el.

If the lockfile ~/.emacs.d/straight/versions/default.el exists, straight.el will make sure the packages match the hashes. This ensures you have a 100% reproducible package management system.

(let ((bootstrap-file (concat user-emacs-directory "straight/repos/straight.el/bootstrap.el"))
      (bootstrap-version 3))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

Install use-package and make use-package use straight.el to fetch packages.

When configuring a feature with use-package, also tell straight.el to install a package of the same name, unless otherwise specified using the :straight keyword.

(straight-use-package 'use-package)
(setq straight-use-package-by-default t)

No littering

emacscollective/no-littering

This section aims to prevent Emacs from littering the file system.

These are the suggested settings.

(use-package no-littering
  :demand
  :config
  (require 'recentf)
  (add-to-list 'recentf-exclude no-littering-var-directory)
  (add-to-list 'recentf-exclude no-littering-etc-directory)
  (setq auto-save-file-name-transforms
        `((".*" ,(no-littering-expand-var-file-name "auto-save/") t)))
  (setq custom-file (no-littering-expand-etc-file-name "custom.el")))

(setq backup-directory-alist '(("." . "~/.saves")))

which-key

(use-package which-key
  :config (which-key-mode 1))

General

All my keybindings are defined with general.el.

To get yourself familiar with keybindings, these functions are useful:

  • describe-bindings
  • general-describe-bindings
  • describe-mode

Much of the general.el config is inspired by Tubo’s .emacs.d.

(use-package general
  :demand t
  :config

  (defvar org-blocks-hidden nil)

  (defun org-toggle-blocks ()
    (interactive)
    (if org-blocks-hidden
        (org-show-block-all)
      (org-hide-block-all))
    (setq-local org-blocks-hidden (not org-blocks-hidden)))

  (defun tmux-capture-pane()
    (interactive)
    (with-output-to-temp-buffer "*tmux-capture-pane*"
      (shell-command "tmux capture-pane -p"
                     "*tmux-capture-pane*"
                     "*Messages*")
      (pop-to-buffer "*tmux-capture-pane*")))

  (general-define-key
   :keymaps 'occur-mode-map
   :states 'normal
   "e" 'occur-edit-mode)

  (general-create-definer my-space-leader-def
    :keymaps 'override
    :states '(normal visual motion emacs)
    :prefix "SPC")

  (my-space-leader-def

    ""     nil

    ;; Things that modify reality to your benefit
    ;; which-func-mode
    "TAB SPC"  'narrow-or-widen-dwim
    "TAB TAB"  'evil-toggle-fold
    ;; "Pull" the code up
    "TAB j"  'evil-open-folds
    ;; "Pull" the code down
    "TAB k"  'evil-close-folds
    ;; Folds everything that doesn't match something
    "TAB s"  'org-sparse-tree
    "TAB e"  'org-toggle-blocks
    "TAB t"  'org-tree-to-indirect-buffer

    ;; Theme operations
    "t"   '(:ignore t :which-key "themes")

    ;; Quit operations
    "q"    '(:ignore t :which-key "quit emacs")
    "qq"  'kill-emacs
    "qz"  'delete-frame

    ;; Buffer operations
    "b"   '(:ignore t :which-key "buffer")
    "bd"  'kill-this-buffer
    ;; I mostly use this binding for reverting dired-narrow (same as :e in Vim)
    "br"  'revert-buffer
    "bo"  'occur

    ;; Help
    "hh"  'info
    "hg"  'general-describe-keybindings
    ;; Man pages in Emacs
    "hm"  'helm-man-woman

    ;; Window operations
    "w"   '(:ignore t :which-key "window")
    "wv"  (lambda () (interactive) (split-window-horizontally) (other-window 1))
    "ws"  (lambda () (interactive) (split-window-vertically) (other-window 1))
    "wu"  'winner-undo
    "wd"  'delete-window
    "wh"  'evil-window-left
    "wl"  'evil-window-right
    "wj"  'evil-window-down
    "wk"  'evil-window-up

    ;; File operations
    "f"   '(:ignore t :which-key "files")
    "fi"  'reload-init-file
    "fj"  'dired-jump
    "fg"  'git-link
    "f SPC"  '((lambda() (interactive)(find-file "~/Projects/org-blog/blog.org")) :which-key "blog.org")
    "fo"  '((lambda() (interactive)(find-file "~/org/org.org")) :which-key "org.org")
    "fc"  '((lambda() (interactive)(find-file "~/org/init.org")) :which-key "init.org")
    "fy"  '((lambda() (interactive)(find-file "~/org/yankpad.org")) :which-key "yankpad.org")

    ;; Applications
    "a"   '(:ignore t :which-key "Applications")
    "ad"  'dired
    ";"   'eval-expression
    "ac"  'calendar

    ;; Org
    "o"   '(:ignore t :which-key "Org")
    "oe"  'org-export-dispatch
    "oa"  'org-agenda
    "oc"  'org-capture
    "ot"  'org-babel-tangle))

As far as I understand, general-create-definer is what you would call a “partial function” (in Python anyway). It wraps general-def with certain default settings.

Evil

Perfect Vim emulation for Emacs.

(use-package evil
  :hook (after-init . evil-mode)
  :init
  (setq evil-want-integration t)  ;; Evil collection
  (setq evil-want-keybinding nil) ;; Evil collection
  :config
  (evil-set-initial-state 'shell-mode 'normal)
  (evil-set-initial-state 'doc-view-mode 'normal)
  (evil-set-initial-state 'package-menu-mode 'normal)
  (evil-set-initial-state 'biblio-selection-mode 'motion)
  (setq doc-view-continuous t))

(use-package evil-commentary
  :general
  ('normal override-global-map
    "gc"  'evil-commentary)
  :after evil
  :config (evil-commentary-mode 1))

evil-collection might override some keybindings set by general.el. See issue 214 on GitHub. I might just do all the keybindings myself, learning them in the process.

evil-collection is a replacement for everything in evil-integration

(use-package evil-collection
  :after evil
  :config
  :config
  (setq evil-collection-mode-list nil)
  (add-to-list 'evil-collection-mode-list '(occur replace))
  (add-to-list 'evil-collection-mode-list 'elfeed)
  (add-to-list 'evil-collection-mode-list 'dired)
  (add-to-list 'evil-collection-mode-list 'docker)
  (evil-collection-init))

I am trying out evil-easymotion.

(use-package evil-easymotion
  :straight t
  :after (evil)
  :custom
  (avy-style 'at-full)
  (avy-background t)
  :config
  (define-key evil-motion-state-map (kbd ",") nil)
  (evilem-default-keybindings (kbd ",,")))

Occur

Interesting article by Aaron Bieber about Occur and Evil.

  • occur-mode-map
  • occur-edit-mode-map

Switch to occur buffer after running occur.

(add-hook 'occur-hook
          '(lambda ()
             (switch-to-buffer-other-window "*Occur*")))

Company

Completion.

(use-package company
  :defer 5
  :diminish
  :commands (company-mode company-indent-or-complete-common)
  :custom
  (company-begin-commands '(self-insert-command))
  (company-idle-delay .1)
  (company-minimum-prefix-length 2)
  (company-show-numbers t)
  (company-tooltip-align-annotations 't)
  (global-company-mode t))

Projectile

(use-package projectile
  :init
  (setq projectile-project-search-path '("~/Projects/"))
  :config
  (projectile-mode +1))

Narrowing

Helm

(use-package helm
  :defer 5
  :config (helm-autoresize-mode 1))

Run helm-dash-install-package to install docsets from kapeli.com - the company behind Dash. This will download HTML files of the docsets to disk. Getting to docs by a web search is an impediment. Don’t do it. DevDocs is an alternative.

(use-package helm-dash
  :commands (helm-dash helm-dash-install-package)
  :general
  (my-space-leader-def
    "hD"  'helm-dash-at-point
    "hd"  'helm-dash)
  :config
  (setq helm-dash-common-docsets '("Python 3")))

Ivy

abo-abo/swiper at GitHub.

(use-package ivy
  :hook (after-init . ivy-mode)
  :config (setq ivy-use-virtual-buffers nil
                ivy-count-format "(%d/%d) "
                ivy-initial-inputs-alist nil
                ivy-re-builders-alist '((t . ivy--regex-ignore-order)))
  :commands (ivy-switch-buffer))
(use-package ivy-posframe
  :custom
  (ivy-posframe-height-alist
   '((swiper . 15)
     (t . 10)))
  (ivy-posframe-display-functions-alist
   '((complete-symbol . ivy-posframe-display-at-point)
     (counsel-describe-function . nil)
     (counsel-describe-variable . nil)
     (swiper . nil)
     (swiper-isearch . nil)
     (t . ivy-posframe-display-at-frame-center)))
  :config
  (ivy-posframe-mode 1))

Counsel

abo-abo/swiper at GitHub.

(use-package counsel
  :after (ivy)
  :general
  (my-space-leader-def
    "SPC" 'counsel-M-x
    "bb"  'counsel-switch-buffer
    "hb"  'counsel-descbinds
    "hf"  'counsel-describe-function
    "hv"  'counsel-describe-variable
    "ff"  'counsel-find-file
    "tt"  'counsel-load-theme
    "fL"  'counsel-locate))

ericdanan/counsel-projectile at GitHub.

Projectile for counsel:

(use-package counsel-projectile
  :after (projectile ivy)
  :config (setq counsel-projectile-sort-files t)
  :general
  (my-space-leader-def
    "p"   '(:ignore t :which-key "projectile")
    "pp"  'counsel-projectile-switch-project))

Docker

(use-package docker)

Version control

M-TAB for magit-section-cycle-diffs is nice. 1, 2, 3 and 4 is also useful. Another useful binding is d d on a file. It will let you stage visual selections.

(use-package magit
  :config
  (add-to-list 'magit-section-initial-visibility-alist '(untracked . hide))
  (add-to-list 'magit-section-initial-visibility-alist '(staged . hide))
  (add-to-list 'magit-section-initial-visibility-alist '(file . hide))
  :general
  (my-space-leader-def
    "g"   '(:ignore t :which-key "git")
    "gs"  'magit-status))

(use-package evil-magit
  :after (magit))
(use-package magit-todos
  :hook (magit-mode . magit-todos-mode))

Setting the foreground and background to the same color. I would like to decrease the width.

(use-package git-gutter-fringe
  :general
  (my-space-leader-def "gS" 'git-gutter:stage-hunk)
  :hook (prog-mode . git-gutter-mode)
  :custom
  (git-gutter-fr:modified-sign "~")
  (git-gutter-fr:added-sign    "+")
  (git-gutter-fr:deleted-sign  "-")
  (git-gutter-fr:disabled-modes '(asm-mode image-mode writeroom-mode))
  :custom-face
  (git-gutter-fr:modified ((t (:foreground "#f1fa8c" :background "#f1fa8c"))))
  (git-gutter-fr:added    ((t (:foreground "#50fa7b" :background "#50fa7b"))))
  (git-gutter-fr:deleted  ((t (:foreground "#ff79c6" :background "#ff79c6"))))
  :config
  (setq git-gutter-fr:side 'right-fringe))

Create links to remote git repositories. git-link will open the current file at the current line in your browser. Kinda similar to browse-at-remote.

(use-package git-link
  :general
  (my-space-leader-def "gl" 'git-link)
  :custom
  (git-link-open-in-browser t))

Org

/r/orgmode

I have not even started using org-agenda, and org-mode is already the greatest thing ever.

One minor issue I have with org is that headers bleed colours from source code blocks if it is the last element within a header.

I would like to learn org-brain soon.

(use-package org
  :straight org-plus-contrib
  :mode ("\\.org\\'" . org-mode)
  :hook (org-mode . org-indent-mode)
  :general
  (my-space-leader-def
    :keymaps 'org-mode-map
    "m"   '(:ignore t :which-key "major")
    "mp"  'counsel-projectile-switch-project)
  :config

  (setq org-startup-indented t
        org-bullets-bullet-list '(" ") ;; no bullets, needs org-bullets package
        org-ellipsis "  " ;; folding symbol
        org-pretty-entities t
        org-hide-emphasis-markers t
        ;; show actually italicized text instead of /italicized text/
        org-agenda-block-separator ""
        org-fontify-whole-heading-line t
        org-fontify-done-headline t
        org-fontify-quote-and-verse-blocks t)

  (setq org-return-follows-link t)

  (setq org-directory "~/org")
  (setq org-default-notes-file (concat org-directory "/org.org"))
  (setq org-agenda-files '("~/org/org.org"))

  (setq org-export-initial-scope 'subtree)

  (setq org-image-actual-width 400)

  ;; I find this much more pleasant
  (setq org-edit-src-content-indentation 0)

  ;; Number of empty lines needed to keep an empty line between
  ;; collapsed trees.
  (setq org-cycle-separator-lines 1)

  ;; This is what I always want
  ;; Open src window in current window
  (setq org-src-window-setup 'current-window)

  ;; = / *
  (setq org-hide-emphasis-markers t)

  ;; Adding <s shortcuts back
  (require 'org-tempo)

  (setq org-babel-python-command "python3")

  ;; Some languages
  (org-babel-do-load-languages 'org-babel-load-languages
                               (append org-babel-load-languages
                                       '((python     . t)
                                         (shell       . t)))))

(use-package org-bullets
  :after org
  :config
  (setq org-bullets-bullet-list
        '("⁖"))
  :hook (org-mode . org-bullets-mode))

From https://github.com/markcol/dot-emacs/blob/e88868e77abea6885f26ef29a04ab222d108877e/init.el

(use-package ob-tmux
  :after org
  :init
  (if (executable-find "tmux")
      (org-babel-do-load-languages
       'org-babel-load-languages
       '((tmux . t)))))

Org babel

(use-package restclient)

(use-package ob-restclient
  :after (org restclient)
  :config
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((restclient . t)))
  :commands org-babel-execute:restclient)
(use-package ob-http
  :after (org restclient)
  :config
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((http . t)))
  :commands org-babel-execute:http)

org-super-agenda

(use-package org-super-agenda
  :config
  (setq org-super-agenda-groups
        '((:name "Today"
                 :time-grid t
                 :scheduled today)
          (:name "Due today"
                 :deadline today)
          (:name "Important"
                 :priority "A")
          (:name "Overdue"
                 :deadline past)
          (:name "Due soon"
                 :deadline future) (:name "Waiting"
                 :todo "WAIT"))))

org-sidebar

(use-package org-sidebar :after org)

org-roam

(use-package org-roam
  :general
  (my-space-leader-def
    "ori"  'org-roam-insert
    "orf"  'org-roam-find-file
    "orr"  'org-roam)
  :hook
  (after-init . org-roam-mode)
  :custom
  (org-roam-directory "~/org/roam/"))

(use-package org-roam-server
  :after org-roam
  :hook
  (org-roam-mode . org-roam-server-mode))

Deft

(use-package deft
  :custom
  (deft-directory "~/org/roam")
  (deft-recursive t)
  :general
  (my-space-leader-def
    "orf"  'deft))

Org noter

Org-noter’s purpose is to let you create notes that are kept in sync when you scroll through the document, but that are external to it - the notes themselves live in an Org-mode file. As such, this leverages the power of Org-mode (the notes may have outlines, latex fragments, babel, etc…) while acting like notes that are made inside the document. Also, taking notes is very simple: just press i and annotate away!

Org-noter is compatible with DocView, PDF Tools, and Nov.el. These modes make it possible to annotate PDF, EPUB, Microsoft Office, DVI, PS, and OpenDocument.

This reminds me of LiquidText for the iPad. It’s also worth having a look at.

Too bad I can’t get this experience with Safari Books Online.

(use-package org-noter :after org)

marklink replaces URLs found in text with a hyperlink containing the HTML <title> tag. Similar to org-cliplink, but I prefer this workflow because I don’t have to context switch when collecting links.

Install it with pipx:

pipx install --spec 'git+https://github.com/staticaland/marklink.git#egg=marklink' marklink
(defun marklink-org ()
  (interactive)
  (save-excursion
    (shell-command-on-region (mark) (point) "marklink --format org" (buffer-name) t)))

Or maybe like this:

(defun mu-xml-format ()
  "Format an XML buffer with `xmllint'."
  (interactive)
  (shell-command-on-region (point-min) (point-max)
                           "xmllint -format -"
                           (current-buffer) t
                           "*Xmllint Error Buffer*" t))

Exporters

CSS

htmlize

(use-package htmlize)

ox-hugo

kaushalmodi/ox-hugo at GitHub.

(use-package ox-hugo
  :config
  (with-eval-after-load 'org-capture
    (defun org-hugo-new-subtree-post-capture-template ()
      (let* ((title (read-from-minibuffer "Post Title: "))
             (fname (org-hugo-slug title)))
        (mapconcat #'identity
                   `(
                     ,(concat "* TODO " title)
                     ":PROPERTIES:"
                     ,(concat ":EXPORT_FILE_NAME: " fname)
                     ":END:"
                     "%?\n")
                   "\n")))

    (add-to-list 'org-capture-templates
                 '("h"                ;`org-capture' binding + h
                   "Hugo post"
                   entry
                   (file+olp "all-posts.org" "Blog Ideas")
                   (function org-hugo-new-subtree-post-capture-template))))
  :after ox)

ox-jira

(use-package ox-jira :after ox)

copy-as-format

github.com/sshaw by Skye Shaw who also has made git-link. Emacs function to copy buffer locations as code blocks in various formats (Slack, JIRA etc).

(use-package copy-as-format
  :commands copy-as-format
  :init
  (setq copy-as-format-default "slack"))

ox-reveal

(use-package ox-reveal
  :config
  (setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js@3.8.0"))

exec-path-from-shell

purcell/exec-path-from-shell at GitHub.

Ensures environment variables inside Emacs look the same as in the user’s shell.

I mainly use this to make my Yubikey GPG key work seamlessly with magit.

(use-package exec-path-from-shell
  :custom
  (exec-path-from-shell-check-startup-files nil)
  (exec-path-from-shell-variables '("PATH" "GOPATH" "SSH_AUTH_SOCK" "SSH_AGENT_PID" "GPG_AGENT_INFO"))
  :config
  (when (memq window-system '(mac ns x))
    (exec-path-from-shell-initialize)))

Aesthetics

Fonts

Inspired by Protesilaos Stavrou and Abhinav Tushar.

I would like to create “font themes” like in mattpalermo/.emacs.d at some point.

(use-package emacs
  :config

  (setq byte-compile-warnings '(cl-functions))

  (setq whitespace-style '(face trailing))

  (defun my-prog-mode-hook ()
    (auto-fill-mode)
    (show-paren-mode)
    (whitespace-mode)
    (electric-pair-mode)
    (display-line-numbers-mode))

  (add-hook 'prog-mode-hook 'my-prog-mode-hook)
  (setq before-save-hook 'nil)
  (add-hook 'before-save-hook 'delete-trailing-whitespace)

  (defun turn-on-variable-pitch-mode ()
    (interactive)

    (variable-pitch-mode 1))

  (defun turn-off-variable-pitch-mode ()
    (interactive)

    (variable-pitch-mode nil))

  (set-face-attribute 'default nil :font "Menlo-14")
  (set-face-attribute 'fixed-pitch nil :font "Menlo-14")
  (set-face-attribute 'variable-pitch nil :font "EtBembo-17")

  (dolist (face '(default fixed-pitch))
    (set-face-attribute `,face nil :font "Menlo-14")))

Modeline

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

Modus themes

protesilaos/modus-themes at GitLab.

(use-package modus-operandi-theme
  :config
  (load-theme 'modus-operandi t))
(use-package modus-vivendi-theme)

Poet themes

kunalb/poet at GitHub.

(use-package poet-theme)

Kaolin themes

Not for me.

(use-package kaolin-themes
  :config
  (load-theme 'kaolin-galaxy t))

zaiste-theme

Requires doom, so this doesn’t work.

(use-package zaiste-theme
  :config
  (load-theme 'modus-operandi t)
  :straight (:host github :repo "zaiste/zaiste-emacs-theme" :files ("zaiste-theme.el")))

Spacemacs themes

nashamri/spacemacs-theme at GitHub.

Not good enough.

(use-package spacemacs-theme :defer t)

Doom themes

hlissner/emacs-doom-themes at GitHub.

Also not good enough, but pretty close.

(use-package doom-themes
  :config
  (setq doom-themes-enable-bold t)
  (setq doom-themes-enable-italic t)
  (doom-themes-org-config))

zerodark-theme

NicolasPetton/zerodark-theme at GitHub

Not good enough.

(use-package zerodark-theme)

Solaire

hlissner/emacs-solaire-mode at GitHub.

solaire-mode is an aesthetic plugin that helps visually distinguish file-visiting windows from other types of windows (like popups or sidebars) by giving them a slightly different - often brighter - background.

(use-package solaire-mode
  :if window-system
  :hook
  ((change-major-mode after-revert ediff-prepare-buffer) . turn-on-solaire-mode)
  (minibuffer-setup . solaire-mode-in-minibuffer)
  :config
  (solaire-global-mode +1)
  (solaire-mode-swap-bg))

Circadian

guidoschmidt/circadian.el at GitHub.

Theme-switching for Emacs based on daytime. Circadian tries to help reducing eye strain that may arise from difference of your display brightness and the surrounding light.

(use-package circadian
  :after doom-themes
  :config
  (setq circadian-themes '(("8:00" . zerodark-theme)
                           ("19:30" . zerodark-theme)))
  (circadian-setup))

Beacon

Malabarba/beacon at GitHub.

A light that follows your cursor around so you don’t lose it!

(use-package beacon
  :config
  (beacon-mode 1)
  (setq beacon-lighter "")
  (setq beacon-blink-delay 0.1)
  (setq bracon-blink-duration 0.50)
  (setq beacon-size 35)
  (setq beacon-blink-when-point-moves-vertically nil)
  (setq beacon-blink-when-point-moves-horizontally nil)
  (setq beacon-blink-when-window-scrolls nil)
  (setq beacon-blink-when-buffer-changes nil)
  (setq beacon-blink-when-focused nil)
  (setq beacon-blink-when-window-changes t))

Icons

domtronn/all-the-icons.el at GitHub.

A utility package to collect various Icon Fonts and propertize them within Emacs.

(use-package all-the-icons)

Distraction free mode

SPC w w for some much needed breathing room. Read Emacs proof-of-concept: tweaked focused writing for Org.

(use-package writeroom-mode
  :general
  (my-space-leader-def "ww"  'writeroom-mode)
  (my-space-leader-def "wp"  'variable-pitch-mode)
  :config
  (setq writeroom-width                   140
        writeroom-maximize-window         nil
        writeroom-extra-line-spacing      5
        header-line-format " "
        global-hl-line-mode nil
        writeroom-bottom-divider-width    0
        ;; writeroom-mode-line               t
        writeroom-fringes-outside-margins nil
        writeroom-global-effects (delq 'writeroom-set-fullscreen writeroom-global-effects)))

Applications

Elfeed

skeeto/elfeed at GitHub.

Elfeed is an extensible web feed reader for Emacs, supporting both Atom and RSS.

Lots of great stuff from heikkil. To automatically add “junk” category, read custom elfeed filter functions.

Perhaps how to set keys?

(use-package elfeed

  :general
  (my-space-leader-def "ae" 'elfeed)
  (general-define-key
   :keymaps 'elfeed-search-mode-map
   :states 'normal
   "1" 'elfeed-mark-all-as-read
   "p" 'ambrevar/elfeed-play-with-mpv)

  :config

  (defun elfeed-mark-all-as-read ()
    "Mark currently shown articles read"
    (interactive)
    (mark-whole-buffer)
    (elfeed-search-untag-all-unread))

  (defun ambrevar/elfeed-play-with-mpv ()
    "Play entry link with mpv."
    (interactive)
    (let ((entry (if (eq major-mode 'elfeed-show-mode) elfeed-show-entry (elfeed-search-selected :single)))
          (quality-arg "")
          (quality-val (completing-read "Max height resolution (0 for unlimited): " '("0" "480" "720") nil nil)))
      (setq quality-val (string-to-number quality-val))
      (message "Opening %s with height≤%s with mpv..." (elfeed-entry-link entry) quality-val)
      (when (< 0 quality-val)
        (setq quality-arg (format "--ytdl-format=[height<=?%s]" quality-val)))
      (start-process "elfeed-mpv" nil "mpv" quality-arg (elfeed-entry-link entry)))))
(use-package elfeed-org
  :after elfeed
  :config
  (elfeed-org)
  (setq rmh-elfeed-org-files (list "~/org/elfeed.org")))

Dired

A great file browser.

Inspired by Pierre Seimandi emacs-config.

(use-package dired
  :straight nil
  :config
  (setq delete-by-moving-to-trash t)
  (setq dired-dwim-target t)

  (if (executable-find "gls")
      (progn
        (setq insert-directory-program "gls")
        (setq dired-listing-switches "-lFaGh1v --group-directories-first"))
    (setq dired-listing-switches "-ahlF"))

  (setq ls-lisp-dirs-first t)

  (use-package diredfl
    :config
    (diredfl-global-mode 1))

  :hook (dired-mode . dired-hide-details-mode)
  :after general)

(use-package dired-subtree
  :general
  ('normal dired-mode-map
           "<tab>" 'dired-subtree-toggle
           "<C-tab>" 'dired-subtree-cycle
           "<backtab>" 'dired-subtree-remove)
  :after dired
  :config
  (setq dired-subtree-cycle-depth 1))

(use-package dired-git-info
  :general
  ('normal dired-mode-map
           ")" 'dired-git-info-mode))
(use-package dired-collapse
  :general
  ('normal dired-mode-map
           "9" 'dired-collapse-mode))

quick-preview:

(use-package quick-preview)

Video of dired-narrow by Mike Zamansky. Use SPC b r to widen (revert-buffer).

(use-package dired-narrow
  :after dired
  :general
  ('normal dired-mode-map
           "/" 'dired-narrow))

dired-open, dired-subtree and dired-narrow is a part of dired-hacks.

(use-package dired-recent
  :general
  (my-space-leader-def "fr"  'my-dired-recent-dirs)
  :config
  (dired-recent-mode 1)
  (defun my-dired-recent-dirs ()
    "Present a list of recently used directories and open the selected one in dired"
    (interactive)
    (let ((dir (ivy-read "Directory: "
                         dired-recent-directories
                         :re-builder #'ivy--regex
                         :sort nil
                         :initial-input nil)))
      (dired dir))))

dired-open adds a mechanism to add “hooks” to dired-find-file that will run before emacs tries its own mechanisms to open the file, thus enabling you to launch other application or code and suspend the default behaviour.

(use-package dired-open
  :after dired
  :config
  (setq open-extensions
      '(("webm" . "mpv")
        ("avi" . "mpv")
        ("mp3" . "mpv")
        ("mp4" . "mpv")
        ("m4a" . "mpv")
        ("mkv" . "mpv")
        ("ogv" . "mpv")
        ("png" . "nomacs")
        ("jpeg" . "nomacs")
        ("jpg" . "nomacs")
        ("pdf" . "zathura")
        ("ods" . "libreoffice")
        ("odt" . "libreoffice")
        ("mobi" . "ebook-viewer")
        ("epub" . "ebook-viewer")))
   (setq dired-open-extensions open-extensions))

osx-trash is pretty handy. If you are working on a remote file system, the file will not be copied to the local trash.

(use-package osx-trash
  :after dired
  :if (eq system-type 'darwin)
  :init
  (osx-trash-setup))

Treemacs

(use-package treemacs
  :general
  (my-space-leader-def
    "ft"  'treemacs))

(use-package treemacs-evil
  :after treemacs evil)

(use-package treemacs-projectile
  :after projectile treemacs)

(use-package treemacs-all-the-icons :after treemacs all-the-icons :config (require ‘treemacs-all-the-icons) (treemacs-load-theme “all-the-icons”))

aggressive-indent-mode

Malabarba/aggressive-indent-mode at GitHub.

Automatically indent Lisp code. Helps me stay sane when writing this config file.

(use-package aggressive-indent
  :hook
  ((emacs-lisp-mode clojure-mode) . aggressive-indent-mode))

Lispy

(use-package lispy
  :defer t
  :diminish (lispy-mode)
  :init
  (add-hook 'emacs-lisp-mode-hook (lambda ()(lispy-mode 1))))

Lispyville

(use-package lispyville
  :commands lispyville-mode
  :init
  (add-hook 'lispy-mode-hook #'lispyville-mode)
  :config
  (lispyville-set-key-theme
   '(operators
     prettify
     text-objects
     additional-motions
     slurp/barf-cp)))

mpv

kljohann/mpv.el at GitHub.

This package is a potpourri of helper functions to control a mpv process via its IPC interface. Really nice to take notes on videos, as you can insert timestamps in org-mode and jump to them with mpv-seek-to-position-at-point.

Inspiration:

(use-package mpv
  :general
  (general-define-key
   :keymaps 'dired-mode-map
   :states 'normal
   "p" 'mpv-play-dired)
  :after org
  :init
  (defun org-mpv-complete-link (&optional arg)
    (replace-regexp-in-string
     "^file:" "mpv:"
     (org-file-complete-link arg)
     t t))

  (org-link-set-parameters
   "mpv"
   :follow #'mpv-play
   :complete #'org-mpv-complete-link)

  :config

  (defun org-timer-item--mpv-insert-playback-position (fun &rest args)
    "When no org timer is running but mpv is alive, insert playback position."
    (if (and
         (not org-timer-start-time)
         (mpv-live-p))
        (mpv-insert-playback-position t)
      (apply fun args)))
  (advice-add 'org-timer-item :around
              #'org-timer-item--mpv-insert-playback-position)

  (defun mpv-play-dired ()
    (interactive)
    (mpv-play (expand-file-name (dired-file-name-at-point)))))

Reading

nov.el

epub reader. Its greatest strength is the ability to easily copy/paste code examples in technical books. Combine this with org-noter.

(use-package nov
  :general
  (general-define-key
   :keymaps 'nov-mode-map
   :states 'normal
   "n" 'nov-next-document
   "p" 'nov-previous-document
   "t" 'nov-goto-toc)
  :mode ("\\.epub\\'" . nov-mode))

reformatter

purcell/reformatter.el at GitHub.

Reformatter produces small, self-contained and separate formatters and minor modes which all work consistently and are individually configured.

Many nice reformatters and ideas from mamapanda.

lassik/emacs-format-all-the-code at GitHub is an interesting alternative.

The config below creates the following functions:

  • black-format-buffer
  • black-format-region
  • black-format-on-save-mode
(use-package reformatter
  :config
  (reformatter-define gofmt :program "gofmt" :args '(""))
  (reformatter-define whaturl-org :program "whaturl" :args '("--format" "org"))
  (reformatter-define whaturl-md :program "whaturl" :args '("--format" "md"))
  (reformatter-define prettier-css :program "prettier" :args '("--parser=css"))
  (reformatter-define prettier-html :program "prettier" :args '("--parser=html"))
  (reformatter-define prettier-javascript :program "prettier" :args '("--parser=babylon"))
  (reformatter-define prettier-json :program "prettier" :args '("--parser=json"))
  (reformatter-define prettier-markdown :program "prettier" :args '("--parser=markdown"))
  (reformatter-define prettier-typescript :program "prettier" :args '("--parser=typescript"))
  (reformatter-define prettier-yaml :program "prettier" :args '("--parser=yaml"))
  (reformatter-define refang :program "defang" :args '("--refang"))
  (reformatter-define defang :program "defang" :args '(""))
  (reformatter-define scalafmt :program "scalafmt" :args '("--stdin"))
  (reformatter-define terraform :program "terraform" :args '("fmt" "-"))
  (reformatter-define black
    :program "black"
    :args '("-" "--quiet"))
  (reformatter-define isort-format
    :program "isort"
    :args '("--apply" "-"))
  (reformatter-define json-format
    :program "jq"
    :args '("--indent" "4"))
  (reformatter-define marklink-format
    :program "marklink"
    :args '("--format" "org"))
  (reformatter-define xml-format
    :program "xmllint"
    :args '("--format" "-")
    :mode nil))

Projectile project root or global.

{
        "singleQuote": true,
        "printWidth": 80,
        "proseWrap": "always",
        "tabWidth": 4,
        "useTabs": false,
        "trailingComma": "none",
        "bracketSpacing": true,
        "jsxBracketSameLine": false,
        "semi": true
}

If you want to put the formatter in the project root, use this method:

(reformatter-define prettier
  :program (concat (projectile-project-root) "node_modules/.bin/prettier")
  :args (list "--stdin" "--stdin-filepath" buffer-file-name)
  :lighter "Prettier")

Common file formats

html

(use-package web-mode
  :mode (("\\.html?\\'" . web-mode))
  :gfhook '(prettier-html-on-save-mode))

css

(use-package css-mode
  :gfhook '(prettier-css-on-save-mode))

js

(use-package js2-mode
  :mode
  (("\\.js$" . js2-mode)
   ("\\.jsx$" . js2-jsx-mode))
  :gfhook '(prettier-javascript-on-save-mode))

Markdown

Makes markdown quite tolerable. Uses the same keybindings as org-mode.

(use-package markdown-mode
  :gfhook '(prettier-markdown-on-save-mode)
  :mode (("README\\.md\\'" . gfm-mode)
         ("\\.md$" . markdown-mode)
         ("\\.pmd$" . markdown-mode)
         ("\\.cbmd$" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode)))

yaml

Mode for “yet another markup language”.

(use-package yaml-mode
  :mode (("\\.yml\\'" . yaml-mode)
         ("\\.yaml\\'" . yaml-mode)))

jinja2

(use-package jinja2-mode
  :mode ("\\.j2\\'" . jinja2-mode))
:mode (".*\\(main\.yml\\|site\.yml\\|encrypted\.yml\\|roles/.+\.yml\\|group_vars/.+\\|host_vars/.+\\)" . yaml-mode))

Ansible

(use-package polymode)
(use-package poly-ansible)

json

(use-package json-mode
  :gfhook '(prettier-json-on-save-mode))

toml

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

Docker

(use-package dockerfile-mode
  :commands (dockerfile-mode)
  :init
  (add-to-list 'auto-mode-alist '("Dockerfile\\'" . dockerfile-mode)))

Terraform

(use-package terraform-mode
  :mode (("\\.tf$" . terraform-mode))
  :config
  (progn
    (add-hook 'terraform-mode-hook #'terraform-format-on-save-mode)
   ))

Help

(use-package helpful
  :after counsel
  :config
  (setq counsel-describe-function-function #'helpful-callable)
  (setq counsel-describe-variable-function #'helpful-variable))

Adds examples to help pages.

(use-package elisp-demos
  :after helpful
  :config (advice-add 'helpful-update :after #'elisp-demos-advice-helpful-update))

Outshine

I’d like to try this sometime, but issue 42 is a showstopper.

(use-package outshine
  :hook ((prog-mode          . outline-minor-mode)
         (outline-minor-mode . outshine-mode)))

Python

There is a lot of stuff from radian.el here. That config is quite incredible.

(use-package python
  :gfhook '(black-on-save-mode)
  :config
  (setq python-fill-docstring-style 'django)

  ;; Default to Python 3. Prefer the versioned Python binaries since
  ;; some systems stupidly make the unversioned one point at Python 2.
  (cond
   ((executable-find "python3")
    (setq python-shell-interpreter "python3"))
   ((executable-find "python2")
    (setq python-shell-interpreter "python2"))
   (t
    (setq python-shell-interpreter "python")))

  (defun radian--python-find-virtualenv ()
    "Find a virtualenv corresponding to the current buffer.
Return either a string or nil."
    (cl-block nil
      (when (and (executable-find "poetry")
                 (locate-dominating-file default-directory "pyproject.toml"))
        (with-temp-buffer
          ;; May create virtualenv, but whatever.
          (when (= 0 (call-process
                      "poetry" nil '(t nil) nil "run" "which" "python"))
            (goto-char (point-min))
            (when (looking-at "\\(.+\\)/bin/python\n")
              (let ((venv (match-string 1)))
                (when (file-directory-p venv)
                  (cl-return venv)))))))
      (when (and (executable-find "pipenv")
                 (locate-dominating-file default-directory "Pipfile"))
        (with-temp-buffer
          ;; May create virtualenv, but whatever.
          (when (= 0 (call-process "pipenv" nil '(t nil) nil "--venv"))
            (goto-char (point-min))
            (let ((venv (string-trim (buffer-string))))
              (when (file-directory-p venv)
                (cl-return venv)))))))))

golang

(use-package go-mode
  :init (add-to-list 'exec-path "/Users/anders/go/bin")
  :gfhook '(gofmt-on-save-mode))

Scala and sbt

(use-package scala-mode
  :mode "\\.s\\(cala\\|bt\\)$"
  :gfhook '(scalafmt-on-save-mode))
(use-package sbt-mode
  :commands sbt-start sbt-command
  :config
  ;; WORKAROUND: https://github.com/ensime/emacs-sbt-mode/issues/31
  ;; allows using SPACE when in the minibuffer
  (substitute-key-definition
   'minibuffer-complete-word
   'self-insert-command
   minibuffer-local-completion-map)
   ;; sbt-supershell kills sbt-mode:  https://github.com/hvesalai/emacs-sbt-mode/issues/152
   (setq sbt:program-options '("-Dsbt.supershell=false"))
)

This actually works:

;; Enable nice rendering of diagnostics like compile errors.
(use-package flycheck
  :init (global-flycheck-mode))

(use-package lsp-mode
  :commands (lsp lsp-deferred)
  :hook (go-mode . lsp-deferred)
  ;; Optional - enable lsp-mode automatically in scala files
  :hook (scala-mode . lsp)
  :config
  (setq lsp-prefer-flymake nil)
  (setq lsp-metals-java-home "/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home"))

(use-package lsp-ui
  :commands lsp-ui-mode
  :config
  (setq lsp-ui-doc-enable nil
        lsp-ui-peek-enable t
        lsp-ui-sideline-enable t
        lsp-ui-imenu-enable t
        lsp-ui-flycheck-enable t)
  )

;; Add company-lsp backend for metals
(use-package company-lsp)

Prescient

raxod502/prescient.el at GitHub.

prescient.el is a library which sorts and filters lists of candidates, such as appear when you use a package like Ivy or Company. Extension packages such as ivy-prescient.el and company-prescient.el adapt the library for usage with various frameworks.

This applies for example to SPC f f (counsel-find-file) where the default is alphabetical ordering. When you activate ivy-prescient-mode you will notice that your recent files show up at the top. I also prefer its fuzzy matching.

The last few candidates you selected are displayed first, followed by the most frequently selected ones, and then the remaining candidates are sorted by length.

;; Package `prescient' is a library for intelligent sorting and
;; filtering in various contexts.
(use-package prescient
  :config
  ;; Remember usage statistics across Emacs sessions.
  (prescient-persist-mode +1))

;; Package `ivy-prescient' provides intelligent sorting and filtering
;; for candidates in Ivy menus.
(use-package ivy-prescient
  :after ivy
  :config
  ;; Use `prescient' for Ivy menus.
  (ivy-prescient-mode +1))

Yankpad

Kungsgeten/yankpad at GitHub.

May be the one thing that will get me to use snippets. Check out tangle templates too.

(use-package yasnippet
  :hook (after-init . yas-global-mode))

(use-package yankpad
  :after (org yasnippet)
  :init
  (setq yankpad-file "~/org/yankpad.org")
  :general
  (my-space-leader-def
    "y"   '(:ignore t :which-key "yankpad")
    "yy"  'yankpad-insert))

Org-rifle

Easily find what you are looking for in org-mode buffers.

(use-package helm-org-rifle)

Narrowing

Emacs narrow-or-widen-dwim.

(use-package emacs
  :config
  (defun narrow-or-widen-dwim (p)
    "Widen if buffer is narrowed, narrow-dwim otherwise.
Dwim means: region, org-src-block, org-subtree, or
defun, whichever applies first. Narrowing to
org-src-block actually calls `org-edit-src-code'.

With prefix P, don't widen, just narrow even if buffer
is already narrowed."
    (interactive "P")
    (declare (interactive-only))
    (cond ((and (buffer-narrowed-p) (not p)) (widen))
          ((region-active-p)
           (narrow-to-region (region-beginning)
                             (region-end)))
          ((derived-mode-p 'org-mode)
           ;; `org-edit-src-code' is not a real narrowing
           ;; command. Remove this first conditional if
           ;; you don't want it.
           (cond ((ignore-errors (org-edit-src-code) t)
                  (delete-other-windows))
                 ((ignore-errors (org-narrow-to-block) t))
                 (t (org-narrow-to-subtree))))
          ((derived-mode-p 'latex-mode)
           (LaTeX-narrow-to-environment))
          (t (narrow-to-defun))))

Lozenge

Kungsgeten/org-lozenge at GitHub.

Pollen inspired lozenge syntax for org-mode. Practical Typography was made using Pollen. The book will make you a better typographer.

(use-package lozenge
  :after org
  :config
  (org-lozenge-enable)
  :straight (:host github :repo "Kungsgeten/lozenge.el"))

My functions

const/window-single-toggle is based on windower by Pierre Neidhardt (ambrevar on GitLab)

(use-package emacs
  :config
  (defvar const/window-configuration nil
    "Current window configuration.
Intended for use by `const/window-monocle'.")

  (define-minor-mode const/window-single-toggle
    "Toggle between multiple windows and single window.
This is the equivalent of maximising a window.  Tiling window
managers such as DWM, BSPWM refer to this state as 'monocle'."
    :lighter " [M]"
    :global nil
    (if (one-window-p)
        (when const/window-configuration
          (set-window-configuration const/window-configuration))
      (setq const/window-configuration (current-window-configuration))
      (delete-other-windows)))

  :general
  (my-space-leader-def
    "wz"  'const/window-single-toggle))

const/insert-pair-completion is used to insert pair characters.

(use-package emacs
  :config
  ;; Got those numbers from `string-to-char'
  (defconst const/insert-pair-alist
    '(("' Single quote" . (39 39))           ; ' '
      ("\" Double quotes" . (34 34))         ; " "
      ("« Guillemet" . (171 187))            ; « »
      ("( Parentheses" . (40 41))            ; ( )
      ("` Elisp quote" . (96 39))            ; ` '
      ("‘ Single apostrophe" . (8216 8217))  ; ‘ ’
      ("“ Double apostrophes" . (8220 8221)) ; “ ”
      ("{ Curly brackets" . (123 125))       ; { }
      ("[ Square brackets" . (91 93))        ; [ ]
      ("< Angled brackets" . (60 62)))       ; < >
    "Alist of pairs for use with `const/insert-pair-completion'.")

  (defun const/insert-pair-completion (&optional arg)
    "Insert pair from `const/insert-pair-alist'."
    (interactive "P")
    (let* ((data const/insert-pair-alist)
           (chars (mapcar #'car data))
           (choice (completing-read "Select character: " chars nil t))
           (left (cadr (assoc choice data)))
           (right (caddr (assoc choice data))))
      (insert-pair arg left right))))

If not region then use point.

https://github.com/stsquad/my-emacs-stuff/blob/7178576491cef557d442aa0947b99ac45f182e71/my-transmission.el#L31

This: (let ((url (thing-at-point 'url)))

(defun const/marklink-urls-at-point-region-paragraph ()
  (interactive)
  (when (thing-at-point 'url t)
    (message "We have URL %s" (thing-at-point 'url t))))

(defun word-or-region-to-lcc ()
  "Convert word at point (or selected region) to lower camel case."
  (interactive)
  (let* ((bounds (if (use-region-p)
                     (cons (region-beginning) (region-end))
                   (bounds-of-thing-at-point 'url)))
         (text   (buffer-substring-no-properties (car bounds) (cdr bounds))))
    (when bounds
      (delete-region (car bounds) (cdr bounds))
      (insert (marklink-format text)))))

(defun const/ivy ()

  (ivy-read "Hello ivy: "
            '("One "
              "Two "
              "Three "
              "Four "))
  )