Last Emacs bankruptcy forced me to try a different route - put everything in org file and have it documented, explained and never stale.
Explanations combined with code in one file is effectuating old paradigm of Literate Programming, which might have its flaws but lisp configuration file is exactly where it shines. I will try not to underestimate “Literate” part of this concept and give thorough explanations for every line of code along with guiding principles for bigger sections.
This is obviously opinionated setup, being my actual working init
, but I made some effort making it modular so it’s possible to pick any part of it separately. Reminding future me on what the hell I was thinking when I included something is part of the reason I’m writing this, but I also hope it can be of help to someone trying to learn how Emacs initialization works. I had tremendous help from other people’s init directories and I hope detailed instructions presented here could help someone in similar fashion.
For somebody who is starting his journey with Emacs, this repository can serve as a starting point for personal setup. Just copying it will leave you with nice looking and reasonable settings. I have a tendency for minimalism so this should be easy to build upon. Similarly, separation of code blocks makes it trivial to remove parts that are of no interest in someone else’s workflow.
If you find something achievable in simpler or more efficient way, please feel free to tell me. This is in no way finished and I don’t expect it ever to be.
This is where the magic happens… Babel extracts code from this file copying it to self-created config/settings.el
which Emacs uses for initialization. Using that extracted code Custom
creates config/custom.el
and lists packages mentioned, preparing them for download and installation.
- Source of all packages is MELPA, after I found out that MELPA Stable is not superior in any way. Emacs package sources are still evolving ecosystem and everything relies on your specific needs, but
MELPA
seem to be best choice for now. - Strategy used for installation is use-package, which to me looks superior to all other techniques (and I tried them all). Possibility to require and define package in the same place where you are setting its behavior does wonders for readability. Some packages might make you jump through hoops if you try to install them this way, but it’s worth it.
In init
we have to set some things beforehand so settings
can run consistently between reboots and there is no clutter created in Emacs root directory.
(package-initialize)
is there just becauseCustom
would put it there anyway being the starting file of initialization.config-dir
anddata-dir
are names of directories thatno-littering
package uses for storing transient files which need to be created in the pre-processing stage.
Before we go on with installing packages it’s essential to configure some things. Everything in this section concerns initializing set-package
and making sure we don’t create clutter in the init directory.
Before installing anything, it’s essential to setup TLS certificate because Emacs is not handling that in ideal way. For openssl to work on OSX we need to install libressl
, which is easiest to do via Homebrew: brew install libressl
. Popular Linux distros have this predefined so there is no need for any setup and for other systems you should easily find equivalent ssl libraries.
(require 'gnutls)
(add-to-list 'gnutls-trustfiles "/usr/local/etc/[email protected]/cert.pem")
Having only one source repository keeps things simple and I don’t need fancy, rare or bleeding edge packages. I used to go with melpa-stable
, but since that time I learned that there is no advantage presumed with “stable” postfix in the name. I can always download packages and set local source for use-package
, making it even more secure if that becomes important. I added jcs-elpa
for some packages that are newer.
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(add-to-list 'package-archives '( "jcs-elpa" . "https://jcs-emacs.github.io/jcs-elpa/packages/") t)
I already explained why I like use-package
as my preferred installer. This setup could obviously work with some other macro, but discrepancies are probable.
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(eval-when-compile
(require 'use-package))
Now we can use use-package
for use-package
!
(use-package use-package
:init
(setq use-package-always-ensure t) ; Try installing automatically
(setq use-package-verbose nil) ; Set to true when interested in load times
(use-package use-package-ensure-system-package :ensure t)) ; Need this because we are in use-package config
no-littering package is the first we are going to install. It’s job is to make sub-directories in .init.d
and save all temporary files there. This reduces clutter and helps with having one place to look in case that something is missing.
/config
is for auto generated files that would end up clutteringinit.el
. Process of installation createssettings.el
andcustom.el
files, but any package that needs configuration files should use this directory to save them./data
serves as temporary directory for all packages. This is place forauto-save
andbackup
, along with any other package that needs to save some transient data.
(use-package no-littering
:init (progn
(setq no-littering-etc-directory config-dir)
(setq no-littering-var-directory data-dir)
:config (progn
(require 'no-littering)
(require 'recentf)
(add-to-list 'recentf-exclude no-littering-var-directory)
(add-to-list 'recentf-exclude no-littering-etc-directory)
(setq backup-directory-alist
`((".*" . ,(no-littering-expand-var-file-name "backup/"))))
(setq auto-save-file-name-transforms
`((".*" ,(no-littering-expand-var-file-name "auto-save/") t)))
(setq custom-file (expand-file-name "custom.el" config-dir))
(when (file-exists-p custom-file)
(load custom-file)))))
Some packages are sending unnecessary warnings while installed through use-package
and it’s bothering me, so this is just for quieter experience with installation. Default value for this variable is :warning
and I boosted it up to :error
.
(setq byte-compile-warnings '(cl-functions))
(setq warning-minimum-level :error)
In this section we are dealing with overall look and behavior of Emacs. Values and packages set here are the ones that will influence every mode in Emacs and it would be good for you to understand what they are doing. I tried to add links to repos or other pages of importance that can shine some light on what given package is trying to achieve.
GUI app should take as much screen real estate as possible.
(custom-set-variables
'(initial-frame-alist (quote ((fullscreen . maximized)))))
I want text cursor looking like bar
(other options include: box
, hollow
, hbar
, nil
). This is purely personal preference, play with it and find what works for you.
(setq-default cursor-type 'bar)
Opening files with unknown extension is best to start in text-mode
and specify later.
(setq initial-major-mode 'text-mode)
This kind of safety is not needed and I want Emacs to load variables when it’s in some directory.
(setq enable-local-variables :all)
Since Emacs 25, there is a built in replacement for linum, we turn it on for programming modes.
(defun display-numbers-hook()
(display-line-numbers-mode 1))
(add-hook 'prog-mode-hook 'display-numbers-hook)
Slightly dim window that is not currently in focus.
(use-package dimmer
:custom (dimmer-fraction 0.2)
:config (dimmer-mode))
Newline at the end is needed in most cases.
(setq require-final-newline t)
(setq mode-require-final-newline t)
I often click on the touchpad by accident and I don’t really need a mouse anyway so I decided to turn it off completely. Steve Purcell packed that functionality into a package so I didn’t need to implement it here.
(use-package disable-mouse
:config (global-disable-mouse-mode))
(setq split-height-threshold nil)
(setq split-width-threshold 200)
If you use Emacs without mouse there is not much need for toolbar, scrollbar or menu.
(tool-bar-mode 0)
(menu-bar-mode -1)
While these screens might be helpful for beginners when they start their journey with Emacs, after a while they become annoyances.
(setq inhibit-startup-message t)
(setq inhibit-splash-screen t)
(setq initial-scratch-message nil)
Expect y/n instead of yes/no when needing confirmation - this really ought to be default.
(fset 'yes-or-no-p 'y-or-n-p)
I never need GUI tooltips in Emacs and can’t imagine type of usage that welcomes it. Same goes for the font selection panel.
(setq tooltip-use-echo-area t)
(unbind-key "s-t")
Beep is frequent, irritating and not at all helpful. Send it to message screen instead of speakers so you still have some kind of visible cue that it happened.
(setq ring-bell-function (lambda () (message "*beep*")))
After trying out different solutions, I’m most comfortable switching windows with Ctrl Tab
, probably because it’s the default way of switching tabs in browsers so I can use the same mental mapping.
(global-set-key [C-S-tab] 'windmove-left) ; move to left window
(global-set-key [C-tab] 'windmove-right) ; move to right window
Ace-window brings some additional options for case when there are more windows.
(use-package ace-window
:config
(global-set-key (kbd "M-o") 'ace-window)
(global-set-key (kbd "M-i") 'ace-swap-window)
(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)))
It might be personal quirk but most frequent use of C-l
command for me is to move cursor position to top of the screen, so I usually type C-l C-l
. Whenever something is repeating, aim for simplification.
(setq recenter-positions '(top middle bottom))
Show current row and column at the bottom of the buffer. This is helpful in most modes and unobtrusive in rest of them.
(setq column-number-mode t)
Only case known to me where you would want unwrapped text is parsing binary files. It’s better to override behavior for those purposes, then to scroll left-right through buffer in all other scenarios.
(global-visual-line-mode 1)
American typist’s convention for end of the sentence can cause trouble in some modes. If you need it just turn on M-x repunctuate-sentences
.
(setq sentence-end-double-space nil)
When file edited in buffer changes from some outside source (say, git reset
), I expect buffer to render that change immediately.
(global-auto-revert-mode t)
Jumping to line with goto-line
can be more ergonomic if you have a preview of where that jump will land you.
(use-package goto-line-preview
:config (global-set-key [remap goto-line] 'goto-line-preview)) ; replace goto-line globally
Confirming or picking exact buffer when trying to kill it wastes time, just leave finger on Control and do it faster with C-x C-k
.
(global-set-key (kbd "C-x C-k") 'kill-this-buffer)
When typing over selected text, I want it replaced and not appended. One of the rare cases when Emacs is in the wrong compared to majority of editors.
(delete-selection-mode 1)
Interesting and efficient way of dealing with undo in Emacs. Takes some time to get used to, but ability to move through undo/redo tree can be great.
(use-package undo-tree
:config (global-undo-tree-mode))
Really simple package, but I find it incredibly useful. Replaces rows of whitespaces with just one or deletes single whitespace. Shortcut is M-Space
.
(use-package shrink-whitespace
:config (global-set-key (kbd "M-SPC") 'shrink-whitespace))
Removing whitespaces in buffer from the end of the lines introduced by you. This is convenient in messy codebases because it doesn’t change other parts of the code.
(use-package ws-butler
:config (ws-butler-global-mode 1))
Whenever the window scrolls a light will shine on top of your cursor so you know where it is.
(use-package beacon
:config
(setq beacon-blink-duration 0.3)
(setq beacon-blink-delay 0.5)
(beacon-mode 1))
Expand or contract selected region by semantic units. Surprisingly usable for both code and text, with language-specific definitions of s-expressions.
(use-package expand-region
:bind
("C->" . 'er/expand-region)
("C-<" . 'er/contract-region))
For now, I only customized things related to OSX because that’s the system I’m spending most of my time in. I plan to do fine tuning for Ubuntu also.
- Bound
Control
toCaps-Lock
key system-wide, not inside Emacs. This is something I encourage everybody to try. Option
isMeta
by default, no need to do anything there.- Left
Cmd
isSuper
by default, no need to do anything there. - Right
Cmd
isControl
, it’s the only key that makes sense for right hand. - Suppress killing and minimizing Emacs with OS shortcuts.
(when (eq system-type 'darwin)
(global-set-key (kbd "s-q") nil)
(global-set-key (kbd "s-w") nil)
(global-set-key (kbd "C-~") nil)
(setq mac-left-command-modifier 'super)
(setq mac-right-command-modifier 'hyper))
There are lot of packages that are trying to influence all aspects of working with Emacs and consequentially change behavior of minibuffer. I tried working with Helm
, but in the end decided I don’t need such an invasive package because I started spending time chasing its quirks around some other big packages.
Another possible route is having just ido-mode
and big number of specialized settings for different scenarios which also tends to become clutter after a while.
For now, I settled with Ivy
which is a little bit more “overall solution” than I’m comfortable with, but it keeps things confined.
Ivy
is split into three packages - Ivy
, Swiper
and Counsel
. Basic functionality of Ivy
is to present list of options as completion mechanism. It’s not strictly bound to minibuffer and it can manage various inputs. Swiper
is enhancement for I-search
, and Counsel
is collection of enhanced Emacs commands. By installing Counsel other two are brought as dependencies, but they all can be used separately.
I made lot of global keybindings for these packages because they are created to replace standard functions of Emacs and enhance them in some way. Good doc for learning about this package can be found here and comprehensive manual is here.
(use-package counsel
:config
(ivy-mode 1) ; Use ivy-mode globally
(setq ivy-use-virtual-buffers t)
(setq ivy-count-format "%d/%d ")
(setq ivy-height 20)
:bind (
;; Ivy bindings
("C-x b" . ivy-switch-buffer)
("C-c z" . ivy-resume)
;; Swiper bindings
("C-s" . swiper) ; replace I-search with swiper version
("C-r" . swiper) ; replace backward I-search with swiper version
("C-c u" . swiper-all) ; search in all opened buffers
;; Counsel bindings
("M-x" . counsel-M-x)
("C-c g" . counsel-ag)
("C-x l" . counsel-locate)
("C-c m" . counsel-imenu)
("C-c o" . counsel-outline)
("C-c t" . counsel-load-theme)
("C-x C-f" . counsel-find-file)
("C-x y" . counsel-find-library)
("C-x p" . counsel-list-processes)
("C-h f" . counsel-describe-function)
("C-h v" . counsel-describe-variable)
("C-h a" . counsel-apropos)
("C-h i" . counsel-info-lookup-symbol)
("C-h u" . counsel-unicode-char)
("C-h b" . counsel-descbinds) ; it hides `describe-bindings` from help.el
("C-h W" . woman) ; not part of counsel, but it belongs with these keybindings
("C-M-y" . counsel-yank-pop)
))
Before ivy-rich, we can install all-the-icons-ivy-rich to present icons in lists. Run M-x all-the-icons-install-fonts
beforehand to download and install them.
(use-package all-the-icons-ivy-rich
:init (all-the-icons-ivy-rich-mode 1)
:config (setq all-the-icons-ivy-rich-icon-size 1.3))
Not really essential, but ivy-rich adds some details to all Ivy results, such as keybindings, descriptions of commands on counsel-M-x
etc.
(use-package ivy-rich
:ensure t
:init (ivy-rich-mode 1))
which-key opens popup after entering incomplete command. Delay of one second gives enough time to finish command without seeing it, and if I’m stuck it shows available endings to entered prefix.
(use-package which-key
:config
(which-key-setup-minibuffer)
(setq which-key-side-window-location 'bottom)
;;(which-key-setup-side-window-right-bottom)
(which-key-mode))
For augmentation of describe functions. It adds lots of valuable information to standard Help
.
(use-package helpful
:custom
(counsel-describe-function-function #'helpful-callable)
(counsel-describe-variable-function #'helpful-variable)
:bind
([remap describe-key] . helpful-key)
([remap describe-command] . helpful-command))
Emacs configuration is job that is never really finished so I added convenient shortcut to open README.org
file from anywhere: C-c i
. When I’m inside README
, it tangles and reloads it again.
(defun djole/load-init ()
"Open main README.org file or reload if it's opened."
(interactive)
(if (equal original-source buffer-file-name) ;; if: I'm already inside README.org
(progn
(org-babel-tangle-file original-source compiled-source) ;; do: recompile
(load-file compiled-source)) ;; and: load again
(find-file original-source))) ;; else: open README
(global-set-key (kbd "C-c i") 'djole/load-init) ;; Add global keybinding for this function
Storing credentials can become complicated, look here for more info.
(setq auth-sources '((:source "~/.authinfo.gpg")))
;; (let ((password (auth-source-pick-first-password :host '("openai.com"))))
;; (message "Password %s" password))
Picking theme is personal for everybody so if you don’t like my choice explore some resources out there and pick one that suits you. There are lot of repositories out there so you shouldn’t limit yourself to base16
, but they do have some variety.
(use-package base16-theme
:if window-system
:config
(load-theme 'base16-solarized-dark t)
; Remove background of code blocks in org files (TODO: Might be better way of doing this)
(custom-set-faces
'(org-block ((t (:background nil))))
'(org-block-begin-line ((t (:background nil))))
'(org-block-end-line ((t (:background nil))))))
;; light candidates: 'base16-mexico-light 'base16-atelier-cave-light
;; dark candidates: 'base16-oceanicnext 'base16-materia 'base16-apathy 'base16-atelier-savanna 'base16-chalk 'base16-google-dark 'base16-gruvbox-dark-pale
Customizing one of the biggest and most popular packages for Emacs could be an infinite job in itself, but I try to go with defaults as much as I can.
Indent everything to the level of its title, but skip further indentation of code.
(setq org-startup-indented t)
(setq org-edit-src-content-indentation 0)
Add some colors to the code using native mode for given language.
(setq org-src-fontify-natively t)
I never accidentally type C-c C-c
so there is no need for confirmation.
(setq org-confirm-babel-evaluate nil)
Tabs should behave in expected way when in code block, default is quite confusing.
(setq org-src-tab-acts-natively t)
Display emphasis immediately: Bold, italic…
(setq org-hide-emphasis-markers t)
Present symbols as intended (pi -> π).
(setq org-pretty-entities t)
org-bullets are presenting nice looking bullets instead of asterisks.
(use-package org-bullets
:config (add-hook 'org-mode-hook 'org-bullets-mode))
While trying to be as close to defaults as possible, I still have some preferences when it comes to customizing org-mode
.
Org mode 9.2 changed structure template expansion, preferred way now is to open popup with C-c C-,
where you can pick template with one letter. I mostly use source with emacs-lisp, so I added it to he list under letter p.
(add-to-list 'org-structure-template-alist '("p" . "src emacs-lisp"))
This package ensures that writing <
as first symbol in line opens up context menu with possible org blocks.
(use-package company-org-block
:ensure t
:custom
(company-org-block-edit-style 'auto) ;; 'auto, 'prompt, or 'inline
(company-begin-commands '(self-insert-command org-self-insert-command))
:hook ((org-mode . (lambda ()
(setq-local company-backends '(company-org-block))
(company-mode +1)))))
I found myself needing to close everything to some level in some org files and it turned out 5 levels is in the sweet spot. This one is a little helper for that.
(defun org-show-five-levels()
(interactive)
(org-content 5))
(add-hook 'org-mode-hook
(lambda ()
(define-key org-mode-map (kbd "C-c 5") 'org-show-five-levels)))
I often want to add checkmark to some places so I created a keybinding
(defun insert-large-checkmark ()
(interactive)
(insert (propertize "✓" 'face '(:height 3.5))))
(with-eval-after-load 'org
(define-key org-mode-map (kbd "C-c x") 'insert-large-checkmark))
Just one way for org-mode
to look nice. I copied most of it from somewhere and added couple of things, but it’s a matter of personal preference so feel free to play with it. One more important note is that layout settings are tightly related to theme you are using, so this section is something you will probably often fine tune.
(let*
((variable-tuple (cond
((x-list-fonts "Source Sans Pro") '(:font "Source Sans Pro"))
((x-list-fonts "Lucida Grande") '(:font "Lucida Grande"))
((x-list-fonts "Verdana") '(:font "Verdana"))
((x-family-fonts "Sans Serif") '(:family "Sans Serif"))
(nil (warn "Cannot find a Sans Serif Font. Install Source Sans Pro."))))
(base-font-color (face-foreground 'default nil 'default))
(headline `(:inherit default :weight normal :foreground ,base-font-color)))
(custom-theme-set-faces 'user
`(org-level-8 ((t (,@headline ,@variable-tuple))))
`(org-level-7 ((t (,@headline ,@variable-tuple))))
`(org-level-6 ((t (,@headline ,@variable-tuple))))
`(org-level-5 ((t (,@headline ,@variable-tuple))))
`(org-level-4 ((t (,@headline ,@variable-tuple))))
`(org-level-3 ((t (,@headline ,@variable-tuple :height 1.33))))
`(org-level-2 ((t (,@headline ,@variable-tuple :height 1.33))))
`(org-level-1 ((t (,@headline ,@variable-tuple :height 1.33))))
`(org-document-title ((t (,@headline ,@variable-tuple :height 1.33 :underline nil))))))
I’m usually exporting to pdf, so ox-pandoc looks like package that covers all my needs. Can’t say that default exporters are pretty, but most of it looks customizable so I will stay with it for now. It needs to have package Pandoc
installed on the system.
(use-package ox-pandoc
:after (org))
(global-set-key (kbd "C-c l") 'org-store-link)
(global-set-key (kbd "C-c a") 'org-agenda)
(global-set-key (kbd "C-c c") 'org-capture)
(setq org-log-done t)
Define default place for my agenda, all files with org
extension inside this directory are taken into account.
(setq org-agenda-files '("~/org/agenda"))
(setq org-default-notes-file "~/org/agenda/notes.org")
(setq org-training-file "~/org/training.org")
Whenever capture is taken, it can be straight refiled to the agenda files. When C-c c
brings out capture template C-c w
can prompt with all the headings from agenda files making it easy to add a new task to the list.
(setq org-refile-use-outline-path 'file) ;; print path starting with file
(setq org-outline-path-complete-in-steps nil) ;; print full paths to items, not just file
(setq org-refile-targets '((org-agenda-files :level . 1))) ;; only two levels should be listed as targets
I use org-journal
to keep my daily diary.
(use-package org-journal
:ensure t
:init
;; Change default prefix key; needs to be set before loading org-journal
(setq org-journal-prefix-key "C-c j ")
:config
(setq org-journal-dir "~/org/journal/"
org-journal-file-format "%Y-%m-%d.org"
org-journal-time-format ""
org-journal-time-prefix ""
org-journal-date-format "%A, %d %B %Y")
:bind (
("s-<left>" . org-journal-previous-entry)
("s-<right>" . org-journal-next-entry)
))
Save todos in the default notes file showing list of headings as possible targets. Training file is organized in weekly format.
(defun refile-heading ()
(interactive)
(org-refile '(4)))
(setq org-capture-templates
'(("d" "Todo" entry (file+function org-default-notes-file refile-heading)
"* TODO %?\nSCHEDULED: %(org-insert-time-stamp nil)")
("t" "Training")
("tw" "Weight" plain (file+datetree+prompt org-training-file "Timeline") "%^{WEIGHT}p" :tree-type week)
("tr" "Running" entry (file+datetree+prompt org-training-file "Timeline")
"* Running\n- distance: %^{Distance} km\n- time: %^{Time} min\n- notes: %^{Notes}" :tree-type week)
("to" "Outdoor Archery" entry (file+datetree+prompt org-training-file "Timeline")
"* Outdoor practice %(concat \":archery:\" (read-string \"Poundage: \") \"lb:\")\n- arrows: %^{Arrows}\n- distance: %^{Distance} m\n- notes: %^{Notes}" :tree-type week)
("tg" "Gym" entry (file+datetree+prompt org-training-file "Timeline")
"* Gym\n%?" :tree-type week)))
Version control is important part of Emacs ever since Magit entered the scene showing factual difference between “porcelain” and “plumbing”. After spending some time getting used to it, Magit
’s efficiency will look like magic to seasoned git user.
Learn it, use it and never look back on days of typing something like:
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
(use-package magit
:bind ("C-x g" . magit-status)
:config
(add-to-list 'magit-no-confirm 'stage-all-changes) ; don't confirm staging all (S)
(setq magit-save-repository-buffers 'dontask)) ; save related buffers when opening magit
git-timemachine lets me browse through previous commits in given file. It’s not used often, but reverting files can be touchy operation and this package presents differences in obvious way.
Using it is easy: M-x git-timemachine
and move through historic revisions of file with p
and n
.
(use-package git-timemachine)
Forge is package used in conjunction with Magit and primarily used for connection with Github
, Gitlab
or similar remotes (forges).
TODO: Make sure it is used and remove if not!
(use-package forge
:after magit)
Treat CamelCase as separate words while editing.
(add-hook 'prog-mode-hook 'subword-mode)
“Complete Anything” or company is used to complete text at point of typing. Make it global and let other packages add appropriate backends.
(use-package company
:config (global-company-mode t))
I fought against using folding for my code because I feel that need for it is major red flag, but programmer’s life is a hard one and I found myself working with company that believes in ruby methods with hundreds of lines of code. This package folds methods so you can see bigger picture.
(use-package yafolding
:config
(add-hook 'prog-mode-hook 'yafolding-mode)
(global-set-key (kbd "C-c y") 'yafolding-discover))
(use-package projectile
:config (projectile-mode)
:custom (projectile-completion-system 'ivy)
:bind-keymap ("C-c p" . projectile-command-map)
:init
(setq projectile-switch-project-action #'projectile-dired))
Main Clojure package.
(use-package cider)
Paredit is used for structural editing, especially important when working with lisps.
(use-package paredit
:config
(add-hook 'emacs-lisp-mode-hook 'paredit-mode)
(add-hook 'lisp-mode-hook 'paredit-mode)
(add-hook 'clojure-mode-hook 'paredit-mode)
(add-hook 'cider-repl-mode-hook 'paredit-mode)
(add-hook 'eval-expression-minibuffer-setup-hook 'paredit-mode))
Good fit with paredit
, forces indentation as you type.
(use-package aggressive-indent
:after paredit
:config
(add-hook 'paredit-mode-hook 'aggressive-indent-mode)
(add-to-list 'aggressive-indent-excluded-modes 'cider-repl-mode))
Colorful presentation of the parenthesis can be of use when there is so many of them.
(use-package rainbow-delimiters
:after paredit
:config (add-hook 'paredit-mode-hook 'rainbow-delimiters-mode))
Showing short flash when sexp is evaluated, adding some clarity to in-place eval.
(use-package cider-eval-sexp-fu
:after paredit)
Common library for opening REPL inside Emacs is inf-ruby, make it available for all ruby files.
(use-package inf-ruby
:init
(add-hook 'ruby-mode-hook 'inf-ruby-minor-mode)
(setq inf-ruby-default-implementation "pry")
:bind
("C-c q" . 'ruby-send-buffer)
("C-c C-q" . 'ruby-send-buffer-and-go)
("C-c r" . 'ruby-send-line)
("C-c C-r" . 'ruby-send-line-and-go))
Use Robe with ruby-mode
, attach it to inf-ruby
subprocess to show info about loaded methods. After configuring robe and company, add company-robe to the list of its backends.
(use-package robe
:init (add-hook 'ruby-mode-hook 'robe-mode)
:bind ("C-M-." . robe-jump)
:config (eval-after-load 'company '(push 'company-robe company-backends)))
Ruby tools brings few refactoring options. I’m still not sure is it worth to include separate package but I’m trying it out.
TODO: Make sure that I’m using Ruby tools or remove it
(use-package ruby-tools
:init (add-hook 'ruby-mode-hook 'ruby-tools-mode))
Minor mode for specs rspec-mode is a great productivity booster when setup correctly. I don’t find default C-c ,
binding convenient in given workflow, so I applied some faster bindings just for this mode.
Various variables are moved in :config
part of the setup for clarity.
(use-package rspec-mode
:config
;; lot of repeating for keybindings, but kept like this for clarity
(define-key rspec-mode-map (kbd "C-q a") 'rspec-verify-all)
(define-key rspec-mode-map (kbd "C-q b") 'rspec-verify-matching)
(define-key rspec-mode-map (kbd "C-q q") 'rspec-verify-single)
(define-key rspec-mode-map (kbd "C-c C-c") 'rspec-verify-single)
(define-key rspec-mode-map (kbd "C-q f") 'rspec-run-last-failed)
(define-key rspec-mode-map (kbd "C-q r") 'rspec-rerun)
(add-hook 'compilation-filter-hook 'inf-ruby-auto-enter); make RSpec get into editing mode on pry.
(setq compilation-scroll-output 'first-error) ; scroll to the first test failure
(setq compilation-ask-about-save nil) ; don't ask for confirmation of save when compiling
(setq compilation-always-kill t)) ; don't ask for confirmation when killing compilation
Rubocop is a static code analyzer, enforcing good practices in coding. After you install rubocop gem (gem install rubocop
) you can add rubocop-emacs to integrate it with Emacs.
(use-package rubocop
:init (add-hook 'ruby-mode-hook 'rubocop-mode))
Part of projectile, projectile-rails helps navigating Rails projects. I added couple of handy keybindings that utilize Super
key along with Control
.
(use-package projectile-rails
:config (projectile-rails-global-mode)
(define-key projectile-rails-mode-map (kbd "C-c r") 'projectile-rails-command-map)
(define-key projectile-rails-mode-map (kbd "C-s-m") 'projectile-rails-find-model)
(define-key projectile-rails-mode-map (kbd "C-s-v") 'projectile-rails-find-view)
(define-key projectile-rails-mode-map (kbd "C-s-c") 'projectile-rails-find-controller)
(define-key projectile-rails-mode-map (kbd "C-s-h") 'projectile-rails-find-helper)
(define-key projectile-rails-mode-map (kbd "C-s-f") 'projectile-rails-find-fixture)
(define-key projectile-rails-mode-map (kbd "C-s-a") 'projectile-rails-find-locale)
(define-key projectile-rails-mode-map (kbd "C-s-r") 'projectile-rails-find-component)
(define-key projectile-rails-mode-map (kbd "C-s-s") 'projectile-rails-find-spec)
(define-key projectile-rails-mode-map (kbd "C-s-l") 'projectile-rails-find-lib)
(define-key projectile-rails-mode-map (kbd "C-s-p") 'projectile-rails-find-current-spec)
(define-key projectile-rails-mode-map (kbd "C-s-i") 'projectile-rails-find-initializer)
(define-key projectile-rails-mode-map (kbd "C-s-j") 'projectile-rails-find-job)
(define-key projectile-rails-mode-map (kbd "C-s-n") 'projectile-rails-find-migration)
(define-key projectile-rails-mode-map (kbd "C-s-g") projectile-rails-mode-goto-map)
(define-key projectile-rails-mode-map (kbd "C-s-<return>") 'projectile-rails-goto-file-at-point))
Use web-mode
for html, erb and various stylesheet files, indent by 2 spaces.
(use-package web-mode
:mode ("\\.erb\\'" ".html?\\'" ".s?css\\'" ".sass\\'")
:config
(setq web-mode-markup-indent-offset 2)
(setq web-mode-css-indent-offset 2)
(setq web-mode-code-indent-offset 2))
Use separate mode for slim files, because web-mode
doesn’t indent as it should. I’m still not happy with the mode but can’t find anything better for now.
(use-package slim-mode
:mode ("\\.slim\\'"))
I tried js2
mode, but upgraded it with rjsx-mode which is derived from it. It’s far from perfect, but such is the state of fast moving front-end standards and old editors.
(use-package rjsx-mode
:mode ("\\.jsx\\'" ".js\\'")
:config
(setq js-indent-level 2)
(setq js2-strict-missing-semi-warning nil)
(define-key rjsx-mode-map "<" nil)
(define-key rjsx-mode-map (kbd "C-d") nil)
(define-key rjsx-mode-map ">" nil))
Vue
files are opened with vue-mode.
(use-package vue-mode
:mode ("\\.vue\\'")
:config
(setq mmm-submode-decoration-level 1)
(setq js-indent-level 2)) ;; 0, 1, or 2 == none, low, and high coloring
Jest for testing front end
(use-package jest-test-mode
:commands jest-test-mode
:init
(add-hook 'typescript-mode-hook 'jest-test-mode)
(add-hook 'js-mode-hook 'jest-test-mode)
(add-hook 'typescript-tsx-mode-hook 'jest-test-mode)
:config
(define-key jest-test-mode-map (kbd "C-c C-c") 'jest-test-run-at-point)
(define-key jest-test-mode-map (kbd "C-q q") 'jest-test-run-at-point)
;(define-key jest-test-mode-map (kbd "C-q a") 'jest-test-run-)
(define-key jest-test-mode-map (kbd "C-q a") 'jest-test-run) ; all in a buffer
(define-key jest-test-mode-map (kbd "C-q p") 'jest-test-run-all-tests)) ; all in a project
Restclient is used in place of Postman or Insomnia. Versatile package, but it takes some practice to get used to it.
(use-package restclient
:mode (("\\.http\\'" . restclient-mode))
:bind (:map restclient-mode-map
("C-c C-f" . json-mode-beautify)))
Installing modes for various markup languages.
(use-package yaml-mode
:mode "\\(\\.\\(yaml\\|yml\\)\\)\\'")
(use-package markdown-mode
:init (setq-default markdown-hide-markup nil))
(use-package json-mode)
(use-package adoc-mode)
UML diagrams can be drawn with Plant UML, using plantuml-mode. Library has to be installed on the system, and its path is loaded with (shell-command-to-string "which plantuml")
.
File plantuml.jar
needs to be copied in the user’s home dir, which is easily done with M-x plantuml-download-jar<RET>
. If this path is to be changed for some reason, both org-plantuml-jar-path
and plantuml-jar-path
variables have to be set here.
(use-package plantuml-mode
:init
(setq plantuml-default-exec-mode 'executable)
(setq plantuml-executable-path (replace-regexp-in-string "\n$" "" (shell-command-to-string "which plantuml")))
(setq org-plantuml-jar-path "~/plantuml.jar")
(add-to-list 'org-src-lang-modes '("plantuml" . plantuml))
(org-babel-do-load-languages 'org-babel-load-languages '((plantuml . t))))
Use eredis for connecting to Redis server.
(use-package eredis)
Try dockerfile-mode.
(use-package dockerfile-mode
:config (add-to-list 'auto-mode-alist '("Dockerfile\\'" . dockerfile-mode)))
Try this for docker-compose. TODO: Check if really needed.
(use-package docker-compose-mode)
Every search reasult or file list can be conveyed into grep buffer
which can be edited with wgrep, effectively giving you power of sed
in Emacs.
(use-package wgrep)
Small package for helping with ansible files, especially with encryption of buffers.
(use-package ansible
:config (add-hook 'yaml-mode-hook 'ansible))
TODO: Explain zettelkasten basics.
Zetteldeft relies on deft, package that improves working with large number of small files.
(use-package deft
:custom
(deft-extensions '("org" "md" "txt"))
(deft-directory "~/org/zettelkasten/")
(deft-use-filename-as-title t))
Zetteldeft looks like the most advanced package for Zettelkasten on Emacs.
(use-package zetteldeft
:after deft
:config
(zetteldeft-set-classic-keybindings)
(setq zetteldeft-title-suffix "\n#+TAGS #"))
English is not my native language so I need more help than some. I still try to keep spellcheck unobtrusive and grammar or style suggestions on minimum so this setting could just be starting point for someone who needs more substantial suggestions or is writing more in natural than programming languages.
flyspell-correct is wrapper for flyspell
with interface that can easily work with ivy
, helm
or simple popup presentation.
Fly-spell
uses separate program to compare words, on Mac it’s easiest to install aspell
which comes with it’s own dictionaries.
Other option is hunspell, but it’s harder for setup because you have to manually put dictionaries in path. Tutorial for usage is available on it’s homepage and you should make sure that you have dictionaries for preferred languages by running hunspell -D
in console, and make sure that one of them is labeled “default”. More explanation about setting Hunspell to work with Emacs can be found here.
I hooked text and org mode with flyspell-mode
, binding correction to C-;
.
(use-package flyspell-correct-popup ; Seems more convenient than `flyspell-correct-ivy` that I used for a long time
:after flyspell
:config
(setq ispell-program-name (executable-find "hunspell"))
(add-hook 'text-mode-hook 'flyspell-mode)
(add-hook 'org-mode-hook 'flyspell-mode)
(define-key flyspell-mode-map (kbd "C-;") #'flyspell-correct-wrapper))
For those who frequently use more than one language, it’s convenient to have that language automatically recognized without need for some headers in files. guess-language does exactly that and can work even in files where languages are mixed. You just put cursor on wanted paragraph and run guess-language
.
I hooked it with flyspell-mode because I use it for switching dictionaries which seems like common usage. My dictionaries obviously will not work for everyone, but it’s fairly easy to change them. Just make sure that names are the same as dictionaries available to `ispell-program-mode` you picked in `fly-spell` section. For example, Hunspell uses en_US
as a name for dictionary, so you have to connect language en
to it.
(use-package guess-language
:after flyspell-correct
:load-path "elpa/guess_tmp/" ;; Temporary line because updated package is still not on MELPA
:config
(add-hook 'flyspell-mode-hook 'guess-language-mode)
(setq guess-language-languages '(en sr sr_LAT))
(setq guess-language-langcodes
'((en . ("en_US" "English")) (sr . ("sr" "Српски")) (sr_LAT . ("sr_LAT" "Srpski")))))
writegood-mode is checking for weasel words, passive voice or duplicates in prose.
(use-package writegood-mode
:init (global-set-key (kbd "C-c w") 'writegood-mode))
Nice little tool that pull definition of the word from wordnik and present it as message
. I bound it to the symbol #
, and it can be called from anywhere with M-#
to define word with cursor on it or prompt for word with C-M-#
.
(use-package define-word
:bind
("M-#" . define-word-at-point)
("C-M-#" . define-word))
Elfeed is the de facto standard for reading RSS feeds. It’s globally bound to C-c f
, because I primarily read my feeds like this. Most of the specific settings for elfeed
are moved to the next heading where I use elfeed-org
. Elfeed saves database in the `~/.emacs.d/data/elfeed` directory which you can delete if you want to start over.
(use-package elfeed
:bind ("C-c f" . elfeed))
I use elfeed-org mostly to load my feeds from the org file. If you choose to do the same thing, make sure to change path to your file and to tag root node in it with :elfeed:
.
(use-package elfeed-org
:after elfeed
:config
(elfeed-org)
(setq rmh-elfeed-org-files (list "~/org/elfeed.org"))
(setq elfeed-org-tree-id "elfeed"))
Focus can be useful for increasing visibility of smaller part of the buffer. Active thing
can be word, paragraph, s-expression… and rest of the text in the buffer gets dimmed to highlight region of interest.
It’s not turned on by default because it can get in the way when reading is the main activity in the buffer.
(use-package focus
:config (define-key focus-mode-map (kbd "C-c f") 'focus-change-thing)) ; override global elfeed keybinding
Spare minutes are best spent on practicing some touch typing and I added some packages that can be helpful.
speed-type takes practicing examples on random which sometimes can be demanding with exotic examples that it puts in front of you.
(use-package speed-type)
typit is convenient for building speed on common words.
(use-package typit)
In this stage, there are many libraries for Emacs that are using ChatGPT. Here is more info: Hacker News, and this list also. I intend to change this section and try every package I run into.
One of the packages, has a bit different layout because chat is formatted as markup.
(use-package gptel
:config (setq gptel-api-key (auth-source-pick-first-password :host '("openai.com"))))
Another lib for chat, maybe a bit better organized.
(use-package chatgpt
:config (setq openai-key (auth-source-pick-first-password :host '("openai.com"))))
Library that produces images from explanation.
(use-package dall-e
:config (setq openai-key (auth-source-pick-first-password :host '("openai.com"))))
Library specialized for code analysis.
(use-package codegpt
:config
(setq codegpt-model "gpt-3.5-turbo")
(setq openai-key (auth-source-pick-first-password :host '("openai.com"))))
;; we recommend using use-package to organize your init.el
(use-package codeium
:init
(add-to-list 'completion-at-point-functions #'codeium-completion-at-point)
:config
(setq use-dialog-box nil) ;; do not use popup boxes
;; get codeium status in the modeline
(setq codeium-mode-line-enable
(lambda (api) (not (memq api '(CancelRequest Heartbeat AcceptCompletion)))))
(add-to-list 'mode-line-format '(:eval (car-safe codeium-mode-line)) t)
;; alternatively for a more extensive mode-line
;; (add-to-list 'mode-line-format '(-50 "" codeium-mode-line) t)
;; use M-x codeium-diagnose to see apis/fields that would be sent to the local language server
(setq codeium-api-enabled
(lambda (api)
(memq api '(GetCompletions Heartbeat CancelRequest GetAuthToken RegisterUser auth-redirect AcceptCompletion))))
;; You can overwrite all the codeium configs!
;; for example, we recommend limiting the string sent to codeium for better performance
(defun my-codeium/document/text ()
(buffer-substring-no-properties (max (- (point) 3000) (point-min)) (min (+ (point) 1000) (point-max))))
;; if you change the text, you should also change the cursor_offset
;; warning: this is measured by UTF-8 encoded bytes
(defun my-codeium/document/cursor_offset ()
(codeium-utf8-byte-length
(buffer-substring-no-properties (max (- (point) 3000) (point-min)) (point))))
(setq codeium/document/text 'my-codeium/document/text)
(setq codeium/document/cursor_offset 'my-codeium/document/cursor_offset))
Installing Github Copilot just to see how it works.
;; Various packages needed for copilot
(let ((pkg-list '(s dash editorconfig)))
(package-initialize)
(when-let ((to-install (map-filter (lambda (pkg _) (not (package-installed-p pkg))) pkg-list)))
(package-refresh-contents)
(mapc (lambda (pkg) (package-install pkg)) pkg-list)))
;; Copilot pulled from the local folder where it was added by git submodule add https://github.com/zerolfx/copilot.el
(use-package copilot
:load-path (lambda () (expand-file-name "copilot.el" user-emacs-directory))
;; don't show in mode line
:diminish)
Some settings for copilot, let’s see how it goes.
(defun rk/no-copilot-mode ()
"Helper for `rk/no-copilot-modes'."
(copilot-mode -1))
(defvar rk/no-copilot-modes '(shell-mode
inferior-python-mode
eshell-mode
term-mode
vterm-mode
comint-mode
compilation-mode
debugger-mode
dired-mode-hook
compilation-mode-hook
flutter-mode-hook
minibuffer-mode-hook
*rspec-compilation*)
"Modes in which copilot is inconvenient.")
(defun rk/copilot-disable-predicate ()
"When copilot should not automatically show completions."
(or rk/copilot-manual-mode
(member major-mode rk/no-copilot-modes)
(company--active-p)))
(add-to-list 'copilot-disable-predicates #'rk/copilot-disable-predicate)
(defvar rk/copilot-manual-mode nil
"When `t' will only show completions when manually triggered, e.g. via M-C-<return>.")
(defun rk/copilot-change-activation ()
"Switch between three activation modes:
- automatic: copilot will automatically overlay completions
- manual: you need to press a key (M-C-<return>) to trigger completions
- off: copilot is completely disabled."
(interactive)
(if (and copilot-mode rk/copilot-manual-mode)
(progn
(message "deactivating copilot")
(global-copilot-mode -1)
(setq rk/copilot-manual-mode nil))
(if copilot-mode
(progn
(message "activating copilot manual mode")
(setq rk/copilot-manual-mode t))
(message "activating copilot mode")
(global-copilot-mode))))
(define-key global-map (kbd "M-C-<escape>") #'rk/copilot-change-activation)
(defun rk/copilot-complete-or-accept ()
"Command that either triggers a completion or accepts one if one
is available. Useful if you tend to hammer your keys like I do."
(interactive)
(if (copilot--overlay-visible)
(progn
(copilot-accept-completion)
(open-line 1)
(next-line))
(copilot-complete)))
(define-key copilot-mode-map (kbd "s-<up>") #'copilot-next-completion)
(define-key copilot-mode-map (kbd "s-<down>") #'copilot-previous-completion)
(define-key copilot-mode-map (kbd "s-[") #'copilot-accept-completion-by-word)
(define-key copilot-mode-map (kbd "s-]") #'copilot-accept-completion-by-line)
(define-key global-map (kbd "s-<return>") #'rk/copilot-complete-or-accept)
(defun rk/copilot-quit ()
"Run `copilot-clear-overlay' or `keyboard-quit'. If copilot is
cleared, make sure the overlay doesn't come back too soon."
(interactive)
(condition-case err
(when copilot--overlay
(lexical-let ((pre-copilot-disable-predicates copilot-disable-predicates))
(setq copilot-disable-predicates (list (lambda () t)))
(copilot-clear-overlay)
(run-with-idle-timer
1.0
nil
(lambda ()
(setq copilot-disable-predicates pre-copilot-disable-predicates)))))
(error handler)))
(advice-add 'keyboard-quit :before #'rk/copilot-quit)