
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.
Canonical links to this document
- HTML version
- const.no/init (courtesy of
ox-hugo
andhugo
) - 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 packagesuse-package
configures and loads packagesgeneral.el
sets keybindingsevil
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:
- Spin Your Own Spacemacs-lite (Spacemacs-lite config here)
- How to build your own spacemacs
- Pinching the best bits from Spacemacs
- Emacs from scratch
- Awesome emacs config files
- The Emacs Lisp Style Guide
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.
- Evil Mode: Or, How I Learned to Stop Worrying and Love Emacs
- Getting Started With Org Mode
- Literate Devops with Emacs
- Org-mode, literate programming in Emacs
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
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
(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
(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
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
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))
- https://github.com/abo-abo/org-fu/blob/master/org-fu.el
- https://github.com/abo-abo/orly/blob/master/orly.el
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
- Reveal Hugo - Hugo Themes
- GitHub - yjwen/org-reveal: Exports Org-mode contents to Reveal.js HTML presentation.
(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
(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
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
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?
- .emacs.d/elfeed.el at 266493c6f9966d4319d8584fe0d558776fb436f2 · zmaas/.emacs.d · GitHub
- dotfiles/_elfeed.el at 719066e05cbe43cfe45a28651b9541f27f6153ae · asakeron/dotfiles · GitHub
- https://github.com/search?q=elfeed-search-mode-map+general-define-key&type=Code
(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
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
- GitHub - jrblevin/markdown-mode: Emacs Markdown Mode
- Markdown Mode for Emacs
- Read Guide to Markdown Mode for Emacs | Leanpub
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
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
(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.
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 "))
)