;; Configuration for Emacs
* Installation
*Dependencies*
Emacs dependencies/libraries are managed via the internal [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Packages.html#Packages][package
management system]]. To initially install packages, open
=~/.emacs.d/init.el=, refresh your package list with =M-x
package-refresh-contents= and install everything using =M-x
eval-buffer=.
* Dependency management
** Define package repositories(archives)
#+BEGIN_SRC emacs-lisp
(require 'package)
(setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
("marmalade" . "https://marmalade-repo.org/packages/")
("melpa" . "https://melpa.org/packages/")
("org" . "https://orgmode.org/elpa/")))
#+END_SRC
** Define packages that are to be installed
List all used third-party packages. Most will be configured further
down in this file, some are used with the default configuration.
#+BEGIN_SRC emacs-lisp
(defvar my-packages '(
auto-complete
blacken
browse-kill-ring
darktooth-theme
elpy
elfeed
elfeed-goodies
evil
evil-escape
evil-leader
evil-mc
evil-numbers
evil-surround
flycheck
flycheck-flow
helm
go-mode
impatient-mode
ledger-mode
lsp-mode
lsp-ui
magit
markdown-mode
pdf-tools
projectile
py-autopep8
org
rainbow-mode
web-mode
which-key
))
#+END_SRC
** Install packages
#+BEGIN_SRC emacs-lisp
(dolist (p my-packages)
(unless (package-installed-p p)
(package-refresh-contents)
(package-install p))
(add-to-list 'package-selected-packages p))
#+END_SRC
* Basics
** History
#+BEGIN_SRC emacs-lisp
(setq savehist-file "~/.emacs.d/savehist")
(savehist-mode +1)
(setq savehist-save-minibuffer-history +1)
(setq savehist-additional-vriables
'(kill-ring
search-ring
regexp-search-ring))
#+END_SRC
** GUI
#+BEGIN_SRC emacs-lisp
(scroll-bar-mode -1)
(tool-bar-mode -1)
(tooltip-mode -1)
(menu-bar-mode -1)
(add-to-list 'default-frame-alist '(font . "Hack-10"))
(load-theme 'whiteboard)
#+END_SRC
** Gnu Elpa TLS Fix
Emacs 26.1 (for example in Debian Buster) requests the GNU Elpa repo
with the wrong TLS version - which makes the request fail. This is a
manual patch for older versions of Emacs. It's fixed from 26.3 and
above upstream.
#+BEGIN_SRC emacs-lisp
(if (string< emacs-version
"26.3")
(setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3"))
#+END_SRC
** Gargabe Collection
Allow 20MB of memory (instead of 0.76MB) before calling garbage
collection. This means GC runs less often, which speeds up some
operations.
#+BEGIN_SRC emacs-lisp
(setq gc-cons-threshold 20000000)
#+END_SRC
** Do not create backup files
#+BEGIN_SRC emacs-lisp
(setq make-backup-files nil)
#+END_SRC
** Auto-Save in =/tmp=
Store backups and auto-saved files in =TEMPORARY-FILE-DIRECTORY= (which
defaults to /tmp on Unix), instead of in the same directory as the
file.
#+BEGIN_SRC emacs-lisp
(setq backup-directory-alist
`((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
`((".*" ,temporary-file-directory t)))
#+END_SRC
** Always follow symlinks
When opening a file, always follow symlinks.
#+BEGIN_SRC emacs-lisp
(setq vc-follow-symlinks t)
#+END_SRC
** Sentences have one space after a period
Don't assume that sentences should have two spaces after
periods.
#+BEGIN_SRC emacs-lisp
(setq sentence-end-double-space nil)
#+END_SRC
** Confirm before closing Emacs
#+BEGIN_SRC emacs-lisp
(setq confirm-kill-emacs 'y-or-n-p)
#+END_SRC
** =dired-mode=
Ability to use =a= to visit a new directory or file in =dired= instead
of using =RET=. =RET= works just fine, but it will create a new buffer
for /every/ interaction whereas =a= reuses the current buffer.
#+BEGIN_SRC emacs-lisp
(put 'dired-find-alternate-file 'disabled nil)
#+END_SRC
Human readable units
#+BEGIN_SRC emacs-lisp
(setq-default dired-listing-switches "-alh")
#+END_SRC
On =C=, recursively copy by default
#+BEGIN_SRC emacs-lisp
(setq dired-recursive-copies 'always)
#+END_SRC
*** =dired-narrow=
=dired-narrow= of the [[https://github.com/Fuco1/dired-hacks][dired-hacks]] repository allows to dynamically
narrow a dired buffer down to contents of interest. A demo can be seen
[[http://pragmaticemacs.com/emacs/dynamically-filter-directory-listing-with-dired-narrow/][on this blog post]].
#+BEGIN_SRC emacs-lisp
(require 'dired)
(define-key dired-mode-map (kbd "/") 'dired-narrow-fuzzy)
#+END_SRC
Commands:
- =/= starts fuzzy matching
- Use the dired buffer as usual
- =g= to go back to the complete file listing
** Ask =y/n= instead of =yes/no=
This is a favorable shorthand.
#+BEGIN_SRC emacs-lisp
(fset 'yes-or-no-p 'y-or-n-p)
#+END_SRC
** Auto revert files on change
When something changes a file, automatically refresh the
buffer containing that file so they can't get out of sync.
#+BEGIN_SRC emacs-lisp
(global-auto-revert-mode t)
#+END_SRC
** Shortcut for changing font-size
#+BEGIN_SRC emacs-lisp
(defun zoom-in ()
(interactive)
(let ((x (+ (face-attribute 'default :height)
10)))
(set-face-attribute 'default nil :height x)))
(defun zoom-out ()
(interactive)
(let ((x (- (face-attribute 'default :height)
10)))
(set-face-attribute 'default nil :height x)))
(define-key global-map (kbd "C-1") 'zoom-in)
(define-key global-map (kbd "C-0") 'zoom-out)
#+END_SRC
** Disable startup message
#+BEGIN_SRC emacs-lisp
(setq inhibit-splash-screen t)
(setq inhibit-startup-message t)
#+END_SRC
** Display the current time
#+BEGIN_SRC emacs-lisp
(display-time-mode t)
#+END_SRC
** Do not display GUI Toolbar
#+BEGIN_SRC emacs-lisp
(tool-bar-mode 0)
#+END_SRC
** Automatic Line Breaks
Do not enable automatic line breaks for all text-mode based hooks,
because several text-modes (markdown, mails) enjoy the pain of long
lines. So here, I only add whitelisted modes sparingly. The other
modes have a =visual-clean= configuration which makes the text look
nice locally, at least.
#+BEGIN_SRC emacs-lisp
(add-hook 'org-mode-hook 'auto-fill-mode)
#+END_SRC
** Enable Narrow To Region
Enable narrow-to-region (C-x n n / C-x n w). This is disabled by
default to not confuse beginners.
#+BEGIN_SRC emacs-lisp
(put 'narrow-to-region 'disabled nil)
#+END_SRC
** Disable scroll bars
#+BEGIN_SRC emacs-lisp
(scroll-bar-mode -1)
#+END_SRC
** Remember the cursor position of files when reopening them
#+BEGIN_SRC emacs-lisp
(setq save-place-file "~/.emacs.d/saveplace")
(setq-default save-place t)
(require 'saveplace)
#+END_SRC
** Set $MANPATH, $PATH and exec-path from shell even when started from GUI helpers like =dmenu= or =Spotlight=
#+BEGIN_SRC emacs-lisp
(exec-path-from-shell-initialize)
#+END_SRC
** =windmove=
Windmove is built into Emacs. It lets you move point from window to
window using Shift and the arrow keys. This is easier to type than
‘C-x o’ when there are multiple windows open.
#+BEGIN_SRC emacs-lisp
(when (fboundp 'windmove-default-keybindings)
(windmove-default-keybindings))
#+END_SRC
** =winner-mode=
Allows to 'undo' (and 'redo') changes in the window configuration with
the key commands ‘C-c left’ and ‘C-c right’.
#+BEGIN_SRC emacs-lisp
(when (fboundp 'winner-mode)
(winner-mode 1))
#+END_SRC
Getting from many windows to one window is easy: 'C-x 1' will do it.
But getting back to a delicate WindowConfiguration is difficult. This
is where Winner Mode comes in: With it, going back to a previous
session is easy.
** Bell
Do not ring the system bell, but show a visible feedback.
#+BEGIN_SRC emacs-lisp
(setq visible-bell t)
#+END_SRC
** AngeFtp
Try to use passive mode for FTP.
Note: Some firewalls might not allow standard active mode. However:
Some FTP Servers might not allow passive mode. So if there's problems
when connecting to an FTP, try to revert to active mode.
#+BEGIN_SRC emacs-lisp
(setq ange-ftp-try-passive-mode t)
#+END_SRC
** eww
When entering eww, use cursors to scroll without changing point.
#+BEGIN_SRC emacs-lisp
(add-hook 'eww-mode-hook 'scroll-lock-mode)
#+END_SRC
** Custom-File
#+BEGIN_SRC emacs-lisp
(setq custom-file "~/.emacs.d/custom-settings.el")
(load custom-file t)
#+END_SRC
** Add guix packages to load-path
* Programming
** General
*** Auto Complete
https://github.com/auto-complete/auto-complete
Basic Configuration
#+BEGIN_SRC emacs-lisp
(ac-config-default)
#+END_SRC
*** Tabs
Set tab width to 2 for all buffers
#+BEGIN_SRC emacs-lisp
(setq-default tab-width 2)
#+END_SRC
Use 2 spaces instead of a tab.
#+BEGIN_SRC emacs-lisp
(setq-default tab-width 2 indent-tabs-mode nil)
#+END_SRC
Indentation cannot insert tabs.
#+BEGIN_SRC emacs-lisp
(setq-default indent-tabs-mode nil)
#+END_SRC
Use 2 spaces instead of tabs for programming languages.
#+BEGIN_SRC emacs-lisp
(setq js-indent-level 2)
(setq coffee-tab-width 2)
(setq python-indent 2)
(setq css-indent-offset 2)
(add-hook 'sh-mode-hook
(lambda ()
(setq sh-basic-offset 2
sh-indentation 2)))
(setq web-mode-markup-indent-offset 2)
#+END_SRC
*** Syntax Checking
http://www.flycheck.org/
Enable global on the fly syntax checking through =flycheck=.
#+BEGIN_SRC emacs-lisp
(add-hook 'after-init-hook #'global-flycheck-mode)
#+END_SRC
*** Manage TODO/FIXME/XXX comments
https://github.com/vincekd/comment-tags
=comment-tags= highlights and lists comment tags such as 'TODO', 'FIXME', 'XXX'.
Commands (prefixed by =C-c t=):
- =b= to list tags in current buffer (comment-tags-list-tags-buffer).
- =a= to list tags in all buffers (comment-tags-list-tags-buffers).
- =s= to jump to tag in current buffer by a word or phrase using reading-completion (comment-tags-find-tags-buffer).
- =n= to jump to next tag from point (comment-tags-next-tag).
- =p= to jump to previous tag from point (comment-tags-previous-tag).
#+BEGIN_SRC emacs-lisp
(setq comment-tags-keymap-prefix (kbd "C-c t"))
(with-eval-after-load "comment-tags"
(setq comment-tags-keyword-faces
`(;; A concrete TODO with actionable steps
("TODO" . ,(list :weight 'bold :foreground "#DF5427"))
;; A non-concrete TODO. We only know something is broken/amiss.
("FIXME" . ,(list :weight 'bold :foreground "#DF5427"))
;; Works, but is a code smell (quick fix). Might break down the line.
("HACK" . ,(list :weight 'bold :foreground "#DF5427"))
;; Assumption that needs to be verified.
("CHECK" . ,(list :weight 'bold :foreground "#CC6437"))
;; Use to highlight a regular, but especially important, comment.
("NOTE" . ,(list :weight 'bold :foreground "#1FDA9A"))
;; Use to highlight a regular, but especially important, comment.
("INFO" . ,(list :weight 'bold :foreground "#1FDA9A"))))
(setq comment-tags-comment-start-only t
comment-tags-require-colon t
comment-tags-case-sensitive t
comment-tags-show-faces t
comment-tags-lighter nil))
(add-hook 'prog-mode-hook 'comment-tags-mode)
#+END_SRC
*** Auto-indent with the Return key
#+BEGIN_SRC emacs-lisp
(define-key global-map (kbd "RET") 'newline-and-indent)
#+END_SRC
*** Highlight matching parenthesis
#+BEGIN_SRC emacs-lisp
(show-paren-mode t)
#+END_SRC
*** Delete trailing whitespace
Delete trailing whitespace in all modes. _Except_ when editing
Markdown, because it uses [[http://daringfireball.net/projects/markdown/syntax#p][two trailing blanks]] as a signal to create a
line break.
#+BEGIN_SRC emacs-lisp
(add-hook 'before-save-hook '(lambda()
(when (not (or (derived-mode-p 'markdown-mode)))
(delete-trailing-whitespace))))
#+END_SRC
*** Code Folding
Enable code folding for programming modes.
- =zc=: Fold
- =za=: Unfold
- =zR=: Unfold everything
#+BEGIN_SRC emacs-lisp
(add-hook 'prog-mode-hook #'hs-minor-mode)
#+END_SRC
*** Line numbers
Enable =linum-mode= for programming modes. For newer versions of
Emacs, use =display-line-numbers-mode=, because it's _much_ faster.
#+BEGIN_SRC emacs-lisp
(add-hook 'prog-mode-hook '(lambda ()
(if (version<= emacs-version "26.0.50")
(linum-mode)
(display-line-numbers-mode))))
#+END_SRC
** Python
*** Basics
#+BEGIN_SRC emacs-lisp
(elpy-enable)
;;(setq elpy-rpc-python-command "~/.virtualenvs/elpy-rpc/bin/python3")
(require 'py-autopep8)
(add-hook 'elpy-mode-hook 'py-autopep8-enable-on-save)
(setq python-shell-interpreter "ptipython"
python-shell-interpreter-args "console --simple-prompt"
python-shell-prompt-detect-failure-warning nil)
(add-to-list 'python-shell-completion-native-disabled-interpreters
"ptipython")
(when (require 'flycheck nil t)
(setq elpy-modules (delq 'elpy-module-flymake elpy-modules))
(add-hook 'elpy-mode-hook 'flycheck-mode))
#+END_SRC
** Go
*** Basics
#+BEGIN_SRC emacs-lisp
(require 'lsp-mode)
(add-to-list 'auto-mode-alist '("\\.go\\'" . go-mode))
(add-hook 'go-mode-hook 'lsp-deferred)
(require 'go-autocomplete)
(require 'auto-complete-config)
(ac-config-default)
#+END_SRC
* org
** General Config
#+BEGIN_SRC emacs-lisp
(global-set-key "\C-cl" 'org-store-link)
(global-set-key "\C-ca" 'org-agenda)
(global-set-key "\C-cb" 'org-iswitchb)
(global-set-key "\C-cc" 'org-capture)
(setq calendar-date-style "european")
;; use fast selection
(setq org-use-fast-todo-selection t)
;; switch state without normal processing
(setq org-treat-S-cursor-todo-selection-as-state-change nil)
#+END_SRC
** Agenda
#+BEGIN_SRC emacs-lisp
;; basic agenda stuff
(setq org-sort-agenda-notime-is-late nil)
(setq org-directory "~/Nextcloud/org")
(setq org-agenda-files '("~/Nextcloud/org"))
(setq org-default-notes-file (concat org-directory "/refile.org"))
(setq diary-file "~/Nextcloud/org/diary")
(setq org-agenda-include-diary t)
(setq org-agenda-skip-deadline-if-done t)
(setq org-reverse-note-order t)
(setq org-sort-agenda-notime-is-late nil)
(setq org-archive-location "~/Nextcloud/org/archive.org::* From %s")
(setq org-refile-targets (quote ((nil :maxlevel . 9)
(org-agenda-files :maxlevel . 9))))
(setq org-agenda-custom-commands
(quote (("N" "Agenda and NEXT TODOs"
((agenda "")
(todo "NEXT")))
("y" "Agenda and All TODOS"
((agenda "")
(alltodo ""))))))
#+END_SRC
** Capture
#+BEGIN_SRC emacs-lisp
(define-key global-map "\C-cc" 'org-capture)
(setq org-default-notes-file "~/Nextcloud/org/refile.org")
(setq org-capture-templates
(quote (("t" "Todo" entry (file "~/Nextcloud/org/todo.org")
"* TODO %?")
("j" "Journal" entry (file+datetree "~/Nextcloud/org/journal.org")
"* %?\nEntered on %U\n %i\n %a")
("e" "Emacs Tip" entry (file+headline "~/Nextcloud/org/emacs-tips.org" "Emacs Tips")
"* %?\n %i\n %a"))))
#+END_SRC
** Tags and faces
#+BEGIN_SRC emacs-lisp
(setq org-todo-keywords
(quote ((sequence "TODO(t)" "NEXT(n)" "|" "DONE(d)")
(sequence "WAITING(w@/!)" "HOLD(h@/!)" "|" "CANCELLED(c@/!)" "PHONE" "MEETING"))))
(setq org-todo-keyword-faces
(quote (("TODO" :foreground "red" :weight bold)
("NEXT" :foreground "blue" :weight bold)
("DONE" :foreground "forest green" :weight bold)
("WAITING" :foreground "orange" :weight bold)
("HOLD" :foreground "magenta" :weight bold)
("CANCELLED" :foreground "forest green" :weight bold)
("MEETING" :foreground "forest green" :weight bold)
("PHONE" :foreground "forest green" :weight bold))))
;; tag stuff automatically dependent on a change of state
(setq org-todo-state-tags-triggers
(quote (("CANCELLED" ("CANCELLED" . t))
("WAITING" ("WAITING" . t))
("HOLD" ("WAITING") ("HOLD" . t))
(done ("WAITING") ("HOLD"))
("TODO" ("WAITING") ("CANCELLED") ("HOLD"))
("NEXT" ("WAITING") ("CANCELLED") ("HOLD"))
("DONE" ("WAITING") ("CANCELLED") ("HOLD")))))
(setq org-priority-faces
'((?A . (:foreground "#CC0000" :background "#FFE3E3"))
(?B . (:foreground "#64992C" :background "#EBF4DD"))
(?C . (:foreground "#64992C" :background "#FFFFFF"))))
(setq org-ellipsis "...")
#+END_SRC
;; custom commands on agenda buffer
#+END_SRC
* Helm
#+BEGIN_SRC emacs-lisp
(require 'helm-config)
(global-set-key (kbd "C-x b") 'helm-mini)
(global-set-key (kbd "M-x") 'helm-M-x)
(global-set-key (kbd "M-y") 'helm-show-kill-ring)
(global-set-key (kbd "C-c h") 'helm-command-prefix)
(global-unset-key (kbd "C-x c"))
(setq helm-M-x-fuzzy-match t)
(setq helm-buffers-fuzzy-matching t)
(setq helm-recentf-fuzzy-match t)
(setq helm-move-to-line-cycle-in-source t)
(setq helm-scroll-amount 5)
(setq helm-ff-file-name-history-use-recentf t)
(helm-mode 1)
#+END_SRC
* Search / Completion
* =evil-mode=
Evil is an extensible Vim layer for Emacs.
This combines the best of both worlds: VIM being a great text-editor
with modal editing through semantic commands and Emacs being a LISP
REPL.
** Enable Evil
#+BEGIN_SRC emacs-lisp
(evil-mode t)
;; Enable "M-x" in evil mode
(global-set-key (kbd "M-x") 'execute-extended-command)
#+END_SRC
** Leader Mode Config
#+BEGIN_SRC emacs-lisp
(global-evil-leader-mode)
(evil-leader/set-leader ",")
(evil-leader/set-key
"w" 'basic-save-buffer
"s" 'flyspell-buffer
"b" 'evil-buffer
"q" 'evil-quit)
#+END_SRC
** Evil Surround, emulating tpope's =surround.vim=
#+BEGIN_SRC emacs-lisp
(require 'evil-surround)
(global-evil-surround-mode 1)
#+END_SRC
** Multiple Cursors
https://github.com/gabesoft/evil-mc
=evil-mc= provides multiple cursors functionality for Emacs when used
with =evil-mode=.
=C-n / C-p= are used for creating cursors, and =M-n / M-p= are used
for cycling through cursors. The commands that create cursors wrap
around; but, the ones that cycle them do not. To skip creating a
cursor forward use =C-t= or =grn= and backward =grp=. Finally use
=gru= to remove all cursors.
*** Enable =evil-mc= for all buffers
#+BEGIN_SRC emacs-lisp
(global-evil-mc-mode 1)
#+END_SRC
** Fast switching between buffers
#+BEGIN_SRC emacs-lisp
(define-key evil-normal-state-map (kbd "{") 'evil-next-buffer)
(define-key evil-normal-state-map (kbd "}") 'evil-prev-buffer)
#+END_SRC
** Increment / Decrement numbers
#+BEGIN_SRC emacs-lisp
(global-set-key (kbd "C-=") 'evil-numbers/inc-at-pt)
(global-set-key (kbd "C--") 'evil-numbers/dec-at-pt)
(define-key evil-normal-state-map (kbd "C-=") 'evil-numbers/inc-at-pt)
(define-key evil-normal-state-map (kbd "C--") 'evil-numbers/dec-at-pt)
#+END_SRC
** Use =j/k= for browsing wrapped lines
#+BEGIN_SRC emacs-lisp
(define-key evil-normal-state-map (kbd "j") 'evil-next-visual-line)
(define-key evil-normal-state-map (kbd "k") 'evil-previous-visual-line)
#+END_SRC
** Paste in Visual Mode
#+BEGIN_SRC emacs-lisp
(define-key evil-insert-state-map (kbd "C-v") 'evil-visual-paste)
#+END_SRC
** Disable =evil-mode= for some modes
Since Emacs is a multi-purpose LISP REPL, there are many modes that
are not primarily (or not at all) centered about text-manipulation.
For those, it is reasonable to disable =evil-mode=, because it will
bring nothing to the table, but might just shadow some keyboard
shortcuts.
#+BEGIN_SRC emacs-lisp
(mapc (lambda (mode)
(evil-set-initial-state mode 'emacs)) '(elfeed-show-mode
elfeed-search-mode
forge-pullreq-list-mode
forge-topic-list-mode
dired-mode
tide-references-mode
image-dired-mode
image-dired-thumbnail-mode
eww-mode))
#+END_SRC
** Unbind M-. and M- in =evil-mode=
=M-.= and =M-,= are popular keybindings for "jump to definition" and
"back". =evil-mode= by default binds those to rather rarely used
functions =evil-repeat-pop-next= and =xref-pop-marker-stack=, for some reason.
#+BEGIN_SRC emacs-lisp
(define-key evil-normal-state-map (kbd "M-.") nil)
(define-key evil-normal-state-map (kbd "M-,") nil)
#+END_SRC
** =evil-escape=
https://github.com/syl20bnr/evil-escape
Escape from insert state and everything else.
#+BEGIN_SRC emacs-lisp
(setq-default evil-escape-delay 0.2)
(setq-default evil-escape-key-sequence "jk")
(evil-escape-mode)
#+END_SRC
This results in the same feature-set like this vim keybinding:
#+BEGIN_SRC vim
"Remap ESC to jk
:imap jk <esc>
#+END_SRC
* Which Key
=which-key= displays available keybindings in a popup.
#+BEGIN_SRC emacs-lisp
(add-hook 'org-mode-hook 'which-key-mode)
(add-hook 'cider-mode-hook 'which-key-mode)
#+END_SRC
Use =which-key= to show VIM shortcuts, too.
#+BEGIN_SRC emacs-lisp
(setq which-key-allow-evil-operators t)
(setq which-key-show-operator-state-maps t)
#+END_SRC