This file is intended to be the configurable point of my Emacs configuration.
The complete configuration is very much inspired by Sacha Chua’s
Emacs Configuration, especially org-mode
, with some stolen bits
and pieces from both Spacemacs/Spaceline and hlissner’s Emacs
config.
This file generates my config by tangling all source code blocks in the text to a single elisp-file.
There is a Makefile that tangles everything that needs to be tangled.
- Clone this repository to
/.emacs.d/
(backup your own configuration first!) - Run
make all
in =~/.emacs.d/ - Profit!
Before doing anything else, let us disable the garbage collector during setup, this saves us some time and we’re not likely to run in to memory issues while setting everything up.
(setq gc-cons-threshold 64000000)
(add-hook 'after-init-hook
;; restore after startup
#'(lambda () (setq gc-cons-threshold 800000)))
When I edit system configuration files I might accidentally find
files as my regular user, even though I need sudo rights to edit
them. This advice makes ido-find-file
automatically open it
through TRAMP
as sudo if it is not writable by my user.
(defadvice find-file (after find-file-sudo activate)
"Find file as root if necessary."
(when (and buffer-file-name
(not (file-writable-p buffer-file-name))
(y-or-n-p "Do you want to edit as sudo?"))
(find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))
First we need to activate the package system and add the melpa repository, if it is not already added.
(unless (executable-find "nix")
(require 'package)
(setq package-user-dir (expand-file-name "elpa" packages-dir)
package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
("melpa" . "https://melpa.org/packages/")))
(package-initialize))
;; We've already enabled the package system.
(setq package-enable-at-startup nil)
Make sure to reload the list of packages by calling M-x
package-refresh-contents
when adding melpa for the first time.
I use use-package
for keeping track of my packages, customising
them and load them lazily.
(unless (or (executable-find "nix")
(package-installed-p 'use-package))
(package-refresh-contents)
(package-install 'use-package))
Make sure that use-package
is available for the byte-compiler.
(eval-when-compile
(require 'use-package))
(require 'bind-key)
If we’re running on NixOS (or another system where Nix is
available), don’t let use-package
download and install packages
by itself.
(when (executable-find "nix")
;; Simple dummy ensure function, it does nothing other than
;; requiring the package.
(setq use-package-ensure-function
(lambda (pkg ensure state) (require pkg))))
(setq use-package-verbose t)
Delight is a nice package for hiding stuff in my modeline.
(use-package delight :ensure t)
We would like Emacs to prefer UTF8 when reading ambiguous bit strings.
(prefer-coding-system 'utf-8)
(when (display-graphic-p)
(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)))
Switching between windows can be a bit of a pain, just cycling
through them with C-x o
is not really good enough. I therefore
use windmove
and buffer-move
to navigate amongst my buffers and
I have them bound to, in my opinion at least, sensible keybindings.
(bind-keys ("<S-left>" . windmove-left)
("C-x <left>" . windmove-left)
("<S-up>" . windmove-up)
("C-x <up>" . windmove-up)
("<S-down>" . windmove-down)
("C-x <down>" . windmove-down)
("<S-right>" . windmove-right)
("C-x <right>" . windmove-right))
Sometimes it is nice to be able to shrink and enlarge windows easily and that is why I’ve bounded them to simple keys:
(bind-keys ("S-C-<left>" . shrink-window-horizontally)
("S-C-<right>" . enlarge-window-horizontally)
("S-C-<down>" . shrink-window)
("S-C-<up>" . enlarge-window))
However, sometimes C-x o
is exactly what we need, but then we can
extend it with ace-window
to make it prompt us for a window when
there is a choice.
(use-package ace-window :ensure t :bind (("C-x o" . ace-window)))
Dired is file manager and browser built into Emacs and it is efficient enough. By default it shows every file, even hidden ones, which results in a lot of noise.
(require 'dired-x)
(setq dired-omit-files "^\\...+$")
(add-hook 'dired-mode-hook (lambda () (dired-omit-mode 1)))
(define-key dired-mode-map (kbd "C-c h") 'dired-omit-mode)
Now, pressing C-c h
will hide all hidden files in Dired.
Sometimes my terminal sends garbled keys to Emacs, to make Emacs
understand the terminal I extend the input-decode-map
.
How to add more keys:
- In the scratch buffer: C-q $COMBINATION
- Add the binding to
input-decode-map
. - Profit!
Credits: This fine answer on Emacs stack exchange
;; shift + arrow keys
(define-key input-decode-map "\[1;2D" [S-left])
(define-key input-decode-map "\[1;2A" [S-up])
(define-key input-decode-map "\[1;2C" [S-right])
(define-key input-decode-map "\[1;2B" [S-down])
;; ctrl + arrow keys
(define-key input-decode-map "\[1;5D" [C-left])
(define-key input-decode-map "\[1;5A" [C-up])
(define-key input-decode-map "\[1;5C" [C-right])
(define-key input-decode-map "\[1;5B" [C-down])
;; meta + arrow keys
(define-key input-decode-map "\[1;3D" [M-left])
(define-key input-decode-map "\[1;3A" [M-up])
(define-key input-decode-map "\[1;3C" [M-right])
(define-key input-decode-map "\[1;3B" [M-down])
;; shift + ctrl + arrow keys
(define-key input-decode-map "\[1;6D" [S-M-left])
(define-key input-decode-map "\[1;6A" [S-M-up])
(define-key input-decode-map "\[1;6C" [S-M-right])
(define-key input-decode-map "\[1;6B" [S-M-down])
;; shift + meta + arrow keys
(define-key input-decode-map "\[1;4D" [S-M-left])
(define-key input-decode-map "\[1;4A" [S-M-up])
(define-key input-decode-map "\[1;4C" [S-M-right])
(define-key input-decode-map "\[1;4B" [S-M-down])
When I run Emacs in terminal mode I still want to be able to copy
stuff to the X clipboard. xclip
is a package that does just that.
(use-package xclip :ensure t :init (xclip-mode t))
Emacs should not create backup files in the current directory.
(setq backup-directory-alist `(("" . ,(concat user-emacs-directory "backups"))))
There are some common things I want to use for all every programming language I code in. For instance I would like error checking and auto-completion when it exists and line indicators that shows if a line is modified, added or removed.
Worth noting is that I turn off Transient Mark mode in Common-Code
minor mode, because I like to use the tag stack for navigation (i.e
activating a mark = temporarily “bookmark” current position,
popping marker stack = jumping to last “bookmark). Besides the
original behaviour (i.e beginning a region) is still available on
C-SPC C-SPC
.
(if (>= emacs-major-version 26)
(add-hook 'prog-mode-hook 'display-line-numbers-mode)
(progn
(add-hook 'prog-mode-hook 'linum-mode)))
(add-hook 'prog-mode-hook (lambda () (transient-mark-mode -1)))
(setq company-idle-delay nil)
Quite a few of the programming languages I work with supports LSP,
Microsoft’s Language Server Protocol, and wiring it prog-mode
makes sense.
;; LSP mode is loaded whenever #'lsp or #'lsp-deferred is called
(use-package lsp-mode :ensure t :commands (lsp-deferred)
:hook (prog-mode . lsp-deferred)
:bind (:map lsp-signature-mode-map ("M-n" . nil))
:config (define-key lsp-mode-map (kbd "C-c l")
(lookup-key lsp-mode-map (kbd "s-l"))))
(use-package yasnippet :after lsp-mode :hook (lsp-mode . yas-minor-mode) :ensure t)
(use-package lsp-ui :after lsp-mode :ensure t
:hook (lsp-mode . lsp-ui-mode)
:bind (:map lsp-mode-map ("C-x C-h ." . lsp-ui-doc-show))
:config (setq lsp-ui-flycheck-enable t
lsp-ui-doc-enable nil
lsp-ui-doc-use-child-frame nil
lsp-ui-sideline-enable nil))
I want trailing white space to be removed automatically before saving.
(add-hook 'prog-mode-hook
(lambda () (add-hook 'before-save-hook #'delete-trailing-whitespace)))
Other minor modes I want to have active in programming modes:
;; Folding of outline
(use-package hs-minor-mode :hook prog-mode :delight :bind (("C-<tab>" . #'hs-toggle-hiding)))
;; Automatically revert file when changed outside of Emacs
(use-package autorevert :delight auto-revert-mode :hook (prog-mode . auto-revert-mode))
(use-package subword-mode :hook prog-mode :delight)
(use-package "whitespace" :hook (prog-mode . whitespace-mode) :delight
:config
;; Make the whitespace style really dark and light on the eyes
;; (for dark themes)
(set-face-attribute 'whitespace-space nil :background nil :foreground "gray30")
(set-face-attribute 'whitespace-hspace nil :background nil :foreground "gray30")
(set-face-attribute 'whitespace-indentation nil :background nil :foreground "gray30")
(set-face-attribute 'whitespace-newline nil :background nil :foreground "gray30")
(set-face-attribute 'whitespace-tab nil :background nil :foreground "gray30")
(setq whitespace-style
'(face tabs tab-mark spaces space-mark trailing missing-newline-at-eof indentation empty lines-tail))
)
(use-package hl-line-mode :hook prog-mode :delight)
(use-package hl-todo :ensure t :delight :hook hl-todo-mode)
(use-package column-number-mode :hook prog-mode :delight)
(use-package projectile :ensure t :delight
:bind (("C-c p" . #'projectile-command-map)
("M-p" . #'projectile-command-map))
:config (projectile-mode))
(use-package flycheck :ensure t :hook (prog-mode . flycheck-mode))
;; Hide modes in modeline
(use-package delight :ensure t)
(use-package company :ensure t :delight :hook (prog-mode . company-mode)
:bind (:map prog-mode-map ("C-c RET" . company-complete)))
(use-package rainbow-delimiters :ensure t :delight :hook (prog-mode . rainbow-delimiters-mode))
(use-package multiple-cursors :ensure t
:bind (:map prog-mode-map
("C-S-c C-S-c" . mc/edit-lines)
("M-n" . mc/mark-next-symbol-like-this)
("M-p" . mc/mark-previous-symbol-like-this)
("C-c M->" . mc/mark-next-like-this)
("C-c M-<" . mc/mark-previous-like-this)
("C-," . mc/mark-pop)
("M-<mouse-1>" . mc/add-cursor-on-click)))
I want to be able to easily run a formatter on my code since I don’t want to have to think about coding style.
(defun format-with-command (command)
"Runs COMMAND on buffer and replaces its content with the output.
Used for integrating formatters that do not have elisp packages."
(save-excursion
(shell-command-on-region
(point-min) ;; starting point in buffer
(point-max) ;; ending point in buffer
command ;; command to run in buffer
(current-buffer) ;; output buffer
t ;; replace contents
(concat "*Custom formatter " command " Error Buffer*")
t))) ;; show error buffer
(defun format-with-command-query ()
"Queries the user for a command to use as a formatter"
(interactive)
(format-with-command (read-from-minibuffer "Formatter command: ")))
(defun format-code (ask?)
"Format buffer using formatter in assoc-list prog-mode-formatters..
prog-mode-formatters is an assoc-list on the form 'major-mode
. formatting-call' and formatting-call is invoked with '(funcall).'
"
(interactive "P")
(if (or (not (boundp 'prog-mode-formatters))
ask?)
(format-with-command-query)
(let ((formatter (assoc major-mode prog-mode-formatters)))
(if (eq nil formatter)
(format-with-command-query)
(funcall (cdr formatter))))))
;; Bind it to our formatting key-binding
(define-key prog-mode-map (kbd "C-c C-f") 'format-code)
For C and Java we want to use Clang-format for formatting, Go and Rust will continue to use their respective *fmt binaries.
(use-package clang-format :commands 'clang-format-buffer :ensure t)
(defvar prog-mode-formatters '((c-mode . clang-format-buffer)
(java-mode . clang-format-buffer)
(go-mode . gofmt)
(rust-mode . rust-format-buffer))
"Alist containing major-mode and formatter pairs.")
I mostly use Git to handle my version control and while it certainly got somewhat of a steep learning curve and a few rough edges here and there I mostly find it intuitive.
To help me manage my Git repositories I use the fantastic package
magit
, which is a Git frontend to Emacs and one of the few Git
frontends I really like.
(use-package magit :bind ("C-x g" . magit-status) :ensure t :defer t)
Git-gutter+
is a package that shows a line’s status (added,
modifid or deleted) in a file that is version controlled by Git.
(use-package git-gutter :ensure t :diminish t
:hook (prog-mode . git-gutter-mode)
:bind (:map prog-mode-map
;; Navigate on hunks
("C-x v n" . git-gutter:next-hunk)
("C-x v p" . git-gutter:previous-hunk)
;; Act on hunks
("C-x v =" . git-gutter:popup-hunk)
("C-x v r" . git-gutter:revert-hunk)
("C-x v t" . git-gutter:stage-hunk)
("C-x v U" . git-gutter:update-all-windows)))
To help me interact with my Github repositories I use forge
.
(use-package forge :after magit :ensure t)
I want to format Rust buffers on save:
(use-package rust-mode :ensure t :hook (rust-mode . lsp-deferred)
:init (setq rust-format-on-save t
rust-format-show-buffer nil ;; don't popup error buffer
rust-format-goto-problem nil)) ;; don't move point to error
This configuration sets up a Go mode where common-code minor mode is enabled together with a plethora of other useful stuff, such as linter and formatters etc.
(use-package go-mode :mode "\\.go\\'" :defer t
:hook ((go-mode . lsp-deferred)
(go-mode . (lambda ()
(add-hook 'before-save-hook #'lsp-format-buffer t t)
(add-hook 'before-save-hook #'lsp-organize-imports t t))))
:bind (:map go-mode-map
("C-c C-f" . format-code) ;; otherwise binds to go-goto* functions
("C-c C-k" . godoc)))
I want to be able to run tests directly from within Emacs.
(use-package gotest :ensure t :after go-mode :config (setq go-test-verbose t)
:bind (:map go-mode-map
("C-c C-t t" . go-test-current-test)
("C-c C-t f" . go-test-current-file)
("C-c C-t p" . go-test-current-project)))
(use-package typescript-mode :ensure t :mode ("\\.tsx?\\'") :hook ((typescript-mode . prettier-js-mode)))
(defun local/prettier-use-local-plugins()
"Search project root for prettier plugins."
(when projectile-mode
(let ((arg (concat "--plugin-search-dir=" (projectile-project-root))))
(setq-local prettier-js-args (append prettier-js-args `(,arg))))))
(use-package prettier-js :ensure t :hook (prettier-js-mode . (lambda () (local/prettier-use-local-plugins))))
;; Enable formatting of typescript files with prettier
(add-to-list 'prog-mode-formatters '(typescript-mode . prettier-js))
When I edit nix files I want to have some syntax highlighting.
(use-package nix-mode :ensure t :mode "\\.nix\\'")
(add-to-list 'prog-mode-formatters '(nix-mode . nix-mode-format))
I want Emacs to automatically download lsp-java and install the Eclipse project’s language server.
(use-package lsp-java :ensure t :hook (java-mode . lsp-deferred))
Give my setup a personal touch.
(setq user-full-name "Jacob Jonsson"
user-mail-address "[email protected]")
I don’t like to type more than necessary, so why do I need to type 1-2 extra letters when the first letter is enough?
(fset 'yes-or-no-p 'y-or-n-p)
I’ve seen the splash screen enough times now, please don’t show it to me anymore.
(setq inhibit-splash-screen t)
Now that I’m trying out Dvorak (Svorak A5) these changes makes the transition between key layouts easier.
;; Bind C-z to C-x
(global-set-key (kbd "C-z") ctl-x-map)
;; Bind C-h to previous-line since C-p is no longer on the same half
;; of the keyboard
(global-set-key (kbd "C-x C-h") help-map)
(global-set-key (kbd "C-h") 'previous-line)
When modifying a file Emacs creates a hidden lock symlink pointing to the modified file. This is probably nice when you don’t want to accidentally open an unsaved and modified file in another Emacs instance, but it also breaks tools that watches file modifications in a directory. Therefore I choose to disable it.
(setq create-lockfiles nil)
There are sometimes when I need to interact with external programs. For instance I sometimes like to open URL’s in a more capable browser than EWW (even though it is very good!).
Firefox is currently my driver of choice.
(setq browse-url-browser-function 'browse-url-firefox
browse-url-new-window-flag t)
It is great that you can start out learning Emacs like a normal person, using the mouse and navigating through the menu and tool bar. However, on a smaller screen I find it a waste of screen space.
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)
I really like the gruvbox-dark
theme. It’s a dark theme with good
contrast and stuff.
;; Load theme
(use-package gruvbox-theme :ensure t
:config (load-theme 'gruvbox-dark-hard t))
The fonts in font-preferences
are the preferred fonts that I use
on my system, in descending order. The first font that is available
will be set as the main font for Emacs.
(use-package cl-lib :ensure t)
(defun font-existsp (font)
"Check to see if the named FONT is available."
(if (null (x-list-fonts font)) nil t))
(defun font-avail (fonts)
"Finds the available fonts."
(cl-remove-if-not 'font-existsp fonts))
(defvar font-preferences
'( "Iosevka"
"Hasklig"
"Inconsolata"
"Fira Code"
"Source Code Pro"
"PragmataPro"))
(unless (eq window-system nil)
(let ((fonts (font-avail font-preferences)))
(unless (null fonts) (progn
(set-face-attribute 'default nil :font (car fonts))
(set-face-attribute 'default nil :weight 'medium)))))
When using Hasklig we can have some degree of ligature support and this is configured below.
(use-package ligature
:load-path "/home/jassob/.emacs.d/site-lisp/ligature.el"
:config
;; Enable the "www" ligature in every possible major mode
(ligature-set-ligatures 't '("www"))
;; Enable traditional ligature support in eww-mode, if the
;; `variable-pitch' face supports it
(ligature-set-ligatures 'eww-mode '("ff" "fi" "ffi"))
;; Enable all Cascadia Code ligatures in programming modes
(ligature-set-ligatures 'prog-mode '("|||>" "<|||" "<==>" "<!--" "####" "~~>" "***" "||=" "||>"
":::" "::=" "=:=" "===" "==>" "=!=" "=>>" "=<<" "=/=" "!=="
"!!." ">=>" ">>=" ">>>" ">>-" ">->" "->>" "-->" "---" "-<<"
"<~~" "<~>" "<*>" "<||" "<|>" "<$>" "<==" "<=>" "<=<" "<->"
"<--" "<-<" "<<=" "<<-" "<<<" "<+>" "</>" "###" "#_(" "..<"
"..." "+++" "/==" "///" "_|_" "www" "&&" "^=" "~~" "~@" "~="
"~>" "~-" "**" "*>" "*/" "||" "|}" "|]" "|=" "|>" "|-" "{|"
"[|" "]#" "::" ":=" ":>" ":<" "$>" "==" "=>" "!=" "!!" ">:"
">=" ">>" ">-" "-~" "-|" "->" "--" "-<" "<~" "<*" "<|" "<:"
"<$" "<=" "<>" "<-" "<<" "<+" "</" "#{" "#[" "#:" "#=" "#!"
"##" "#(" "#?" "#_" "%%" ".=" ".-" ".." ".?" "+>" "++" "?:"
"?=" "?." "??" ";;" "/*" "/=" "/>" "//" "__" "~~" "(*" "*)"
"\\\\" "://"))
;; Enables ligature checks globally in all buffers. You can also do it
;; per mode with `ligature-mode'.
(global-ligature-mode t))
After having run with ido and smex for a while I wanted to try out Helm and while it worked quite satisfactorily I thought that it deviated too much from vanilla Emacs experience. My hope is that Ivy and counsel will be a more discrete mix.
;; Enable ivy on completion-read
(use-package ivy :ensure t :init (ivy-mode t))
;; Replace common functions with ivy-versions
(use-package counsel :ensure t
:bind (("M-x" . counsel-M-x)
("C-c g" . counsel-git))
:config (counsel-mode t))
(use-package amx :after counsel :ensure t)
;; Enable ivy powered search/occur function
(use-package swiper :ensure t :bind ("C-s" . swiper))
I sometimes use Imenu to quickly navigate inside the current file.
(define-key global-map (kbd "M-g M-m") #'imenu)
Begin the conditional loading:
(with-eval-after-load 'org
I prefer to have my org files in my ~/personal
(setq org-directory (file-name-as-directory (expand-file-name "~/personal"))
org-default-notes-file (concat org-directory "organizer.org"))
To keep track of my notes and tasks I add some states that my
notes and tasks could be in. For instance in my reading file,
items could be READ, READING or WANT-TO-READ. The letters inside
the parantheses defines keyboard shortcuts that can be used for
selecting the state of the item. The special characters @
and
!
defines how logging should be performed. Changing the state
of an item to a state with a @
prompts you for a note and !
tells org that it should automatically log timestamp of the state
change.
(setq org-todo-keywords
'((sequence "IDEAS(i)" "TODO(t)" "URGENT(u@/!)"
"IN-PROGRESS(p!/@)" "WAITING(w@/@)"
"|" "DONE(d@)" "CANCELLED(c@)")
(sequence "WANT-TO-READ(@)" "READING(!)" "|" "READ(@)")))
Many GTD-apps organize the tasks into projects and contexts, this
is of course doable inside Org mode
as well.
(setq org-tag-alist '(("@work" . ?w) ("@study" . ?s) ("@coding" . ?c)
("@reading" . ?r) ("@home" . ?h)))
When I use org-gcal to synchronize my calendar with Emacs I want those files to end up in my calendar.
(setq org-agenda-files (list org-directory (concat org-directory "/calendar")))
This is the structure of org files that I want to have and try to maintain.
#<<org-files>>
organizer.org | Main org file, used for org-capture and tasks etc |
people.org | People-related tasks |
journal.org.gpg | Journal entries (encrypted) |
studies.org | Chalmers-related tasks |
reading.org | Org file for book notes |
watching.org | Org file for stuff I’d like to watch |
I want to start using org-capture
to quickly add tasks and notes
and organize them in my life.
Quick legend of the template escape codes:
%^{PROMPT}
- Org will prompt me with “PROMPT: ” and the input will replace the occurrance of%^{Task}
in the template,%?
- Org will put the cursor here so I can edit the capture before refiling it,%i
- Org will insert the marked region from before the capture here,%a
- Org will insert an annotation here (,%U
- Org will insert an inactive timestamp here,%l
- Org will insert a literal link here,
(with-eval-after-load 'org
(setq org-capture-templates
`(("t" "Tasks" entry (file+headline ,org-default-notes-file "Inbox")
"* TODO %^{Task}\nCaptured %<%Y-%m-%d %H:%M> %a\n%?\n\n%i\n")
("i" "Interrupting task" entry
(file+headline ,org-default-notes-file "Inbox")
"* IN-PROGRESS %^{Task}\n" :clock-in)
("j" "Journal entry" plain
(file+datetree ,(concat org-directory "journal.org.gpg"))
"%K - %a\n%i\n%?\n")
("J" "Journal entry with date" plain
(file+datetree+prompt ,(concat org-directory "journal.org.gpg"))
"%K - %a\n%i\n%?\n")
("B" "Book" entry
(file+headline ,(concat org-directory "reading.org") "Books")
"* WANT-TO-READ %^{Title} %^g\n\n%i%?\n\n*Author(s)*: %^{Author}\n*Review on:* %^t\n%a %U\n")
("A" "Article" entry
(file+headline ,(concat org-directory "reading.org") "Articles")
"* WANT-TO-READ %^{Title} %^g\n\n*Author(s)*: %^{Author}\n\n*Abstract*: %i%?\n\n[[%l][Link to paper]]\n")
("p" "Blog post" entry
(file+headline ,(concat org-directory "reading.org") "Blog entries")
"* WANT-TO-READ %^{Title} %^g\n\n%i\n\n*Author(s)*: %^{Author}\n\n[[%l][Link to blog post]]\n")
("l" "Bookmark" entry
(file+headline ,(concat org-directory "bookmarks.org") "Captured entries")
"* [[%^{Link}][%^{Title}]]\n\n%i%?\n")
("n" "Notes" entry (file+datetree ,org-default-notes-file) "* %?\n\n%i\n%U\n")
;; Org protocol handlers
("pp" "Protocol Blog post" entry
(file+headline ,(concat org-directory "reading.org") "Blog entries")
"* WANT-TO-READ %:description %^g\n\n%i\n\n*Author(s)*: %^{Author}\n\n[[%l][Link to blog post]]\n")
("c" "Protocol selection" entry (file+headline ,org-default-notes-file "Inbox")
"* [[%:link][%:description]] \n\n#+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n%?\n\nCaptured: %U\n")))
(bind-key "C-M-r" 'org-capture))
I then want to be able to capture stuff from the web using
org-protocol
.
(require 'org-protocol)
(setq org-protocol-protocol-alist org-protocol-protocol-alist-default)
I want to be able to view my org documents so that I can see my progress and what I’ve got left to do and so on. Org publish works rather well for this scenario, even though I probably would like do some automation on when it does the publishing.
(with-eval-after-load 'org
(require 'ox-html)
(setq org-publish-project-alist
`(("html"
:base-directory ,org-directory
:base-extension "org"
:publishing-directory "/ssh:jassob:/var/www/org"
:recursive t
:publishing-function org-html-publish-to-html)
("org-static"
:base-directory ,org-directory
:base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
:publishing-directory "/ssh:jassob:/var/www/org"
:recursive t
:publishing-function org-publish-attachment)
("archive"
:base-directory ,org-directory
:base-extension "org_archive"
:publishing-directory "/ssh:jassob:/var/www/org/archive"
:publishing-function org-html-publish-to-html)
("web"
:base-directory ,(concat org-directory "web/")
:base-extension "org"
:publishing-directory "/ssh:jassob:/var/www/"
:publishing-function org-html-publish-to-html)
("jassob" :components ("html" "archive" "org-static" "web"))
("all" :components ("jassob"))))
(defun local/publish-jassob ()
"Publishes \"jassob\" project"
(interactive)
(org-publish "jassob" t))
(defun local/publish-chalmers ()
"Publishes \"chalmers\" project"
(interactive)
(org-publish "chalmers" t))
(defun local/publish-web ()
"Publishes \"web\" project"
(interactive)
(org-publish "web" t)))
End conditional loading for org config
)
I’m experimenting with EVIL mode in an attempt to learn Vi(m)
keybindings. Since I am used to quitting and escaping stuff by C-g
I want EVIL to transition to normal mode when I press C-g
.
(defun evil-keyboard-quit ()
"Keyboard quit and force normal state."
(interactive)
(and evil-mode (evil-force-normal-state))
(keyboard-quit))
When EVIL is loaded I therefore want bind C-g
to
evil-keyboard-quit
.
(use-package evil :commands 'evil-mode
:bind
(:map evil-normal-state-map ("C-g" . #'evil-keyboard-quit))
(:map evil-motion-state-map ("C-g" . #'evil-keyboard-quit))
(:map evil-motion-state-map ("C-g" . #'evil-keyboard-quit))
(:map evil-insert-state-map ("C-g" . #'evil-keyboard-quit))
(:map evil-window-map ("C-g" . #'evil-keyboard-quit))
(:map evil-operator-state-map ("C-g" . #'evil-keyboard-quit)))
To help me remember my commands I use which-key
, which displays a
popup showing all the keybindings belonging to a prefix key.
(use-package which-key :delight t :ensure t :config (setq which-key-idle-delay 2.0))
(which-key-mode t)
I find Emacs default undo behaviour rather intuitive (of course a
redo is just an undo of your last undo!), but I like being able to
visualise the timeline of my file. Enters undo-tree-mode
!
;; Display local file history as tree of edits
(use-package undo-tree :ensure t :delight
:config
(setq undo-tree-visualizer-timestamps t
undo-tree-visualizer-diff t)
(global-undo-tree-mode))
Sometimes I just want to have my code (or whatever I’m currently
reading or writing) presented to me without any other distractions
and this is where writeroom-mode
(found here) comes in to play.
(use-package writeroom-mode :ensure t
:init (setq writeroom-width 120)
(add-hook 'writeroom-mode-hook (lambda () (display-line-numbers-mode -1)))
:bind (:map writeroom-mode-map
("C-c C-w <" . #'writeroom-decrease-width)
("C-c C-w >" . #'writeroom-increase-width)
("C-c C-w =" . #'writeroom-adjust-width)
("s-?" . nil)
("C-c C-w SPC" . #'writeroom-toggle-mode-line))
(:map global-map
("C-c C-M-w" . #'writeroom-mode)))
Mu4e is a great mail user agent for Emacs, which lets us read, manage and compose mail.
Mu4e requires that the mail indexer mu is installed (and is usually installed in the same package). For Ubuntu/Debian distros the package is installed with:
sudo apt install mu4e
After that we need to make Emacs able to find mu4e package:
(add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e/")
Now let’s continue to set up my maildir directory (which is populated by the isync configuration in https://github.com/Jassob/dotfiles/blob/master/nix/.config/nixpkgs/home.nix#L342) and some other settings:
(with-eval-after-load 'mu4e
(setq mu4e-maildir (expand-file-name "~/.mail/")
mu4e-get-mail-command "mbsync -a"
;; prefer plain-text mail
mu4e-view-prefer-html nil
;; enable inline images
mu4e-view-show-images t
;; show full addresses in view message (instead of just names)
;; toggle per name with M-RET
mu4e-view-show-addresses 't
mu4e-update-interval nil
mu4e-headers-auto-update t
;; every new email composition gets its own frame
mu4e-compose-in-new-frame t
mu4e-compose-signature-auto-include nil
mu4e-compose-format-flowed t
mu4e-compose-dont-reply-to-self t
mu4e-compose-context-policy 'always-ask
mu4e-context-policy 'pick-first
message-kill-buffer-on-exit t
;; important to get mu4e to play with mbsync
mu4e-change-filenames-when-moving t
;; don't save message to Sent Messages, IMAP takes care of this
mu4e-sent-messages-behavior 'delete
mu4e-confirm-quit nil)
;; to view selected message in the browser, no signin, just html mail
(add-to-list 'mu4e-view-actions '("ViewInBrowser" . mu4e-action-view-in-browser) t)
;; use imagemagick, if available
(when (fboundp 'imagemagick-register-types)
(imagemagick-register-types))
(add-hook 'mu4e-view-mode-hook #'visual-line-mode)
;; increase luminesence for dark themes
(setq shr-color-visible-luminance-min 80))
org-mu4e
is a package that let’s us format the messages in
org-mode and then convert them to HTML.
(with-eval-after-load 'mu4e
(require 'org-mu4e)
;; convert org mode to HTML automatically
(setq org-mu4e-convert-to-html t
;;store link to message if in header view, not to header query
org-mu4e-link-query-in-headers-mode nil))
When we compose new messages we want to let the recepient’s email reader break the lines and not force our line width upon them. We also want to enable the spell checker.
(with-eval-after-load 'mu4e
(require 'org-mime)
(add-hook 'mu4e-compose-mode-hook
(lambda ()
(visual-line-mode)
(org-mu4e-compose-org-mode)
(use-hard-newlines -1)
(flyspell-mode))))
Both my mail accounts are rather similar, so I’ll define a function to reduce the repetition needed to define them:
(defun my/make-mu4e-context (name email-address maildir &rest vars)
"Create a mu4e context called NAME based in MAILDIR and uses
EMAIL-ADDRESS.
- NAME is shown when switching between different contexts and the first letter is also the shortcut.
- EMAIL-ADDRESS is used for authenticating with both IMAP and SMTP servers.
- MAILDIR is the subdirectory inside mu4e-maildir that contains the mail for this account, it should start with a /.
"
(make-mu4e-context
:name name
:enter-func (lambda () (mu4e-message (concat "Entering context " name)))
:leave-func (lambda () (mu4e-message (concat "Leaving context " name)))
:match-func (lambda (msg)
(when msg
(mu4e-message-contact-field-matches msg '(:from :to :cc :bcc) user-mail-address)))
:vars `((user-mail-address . ,email-address)
(mu4e-sent-folder . ,(concat maildir "/sent"))
(mu4e-drafts-folder . ,(concat maildir "/sent"))
(mu4e-trash-folder . ,(concat maildir "/trash"))
(mu4e-refile-folder . ,(concat maildir "/all"))
(mu4e-maildir-shortcuts . ((,(concat maildir "/Inbox") . ?i)
(,(concat maildir "/all") . ?a)
(,(concat maildir "/starred") . ?s)))
(mu4e-bookmarks . ((,(concat "flag:unread AND NOT flag:trashed maildir:" maildir "*") "Unread messages" ?u)
("flag:unread AND NOT flag:trashed" "All unread messages" ?a)
("flag:flagged AND NOT flag:trashed" "Starred messages" ?s)
("date:today..now AND NOT flag:trashed" "Today's messages" ?t)
("date:7d..now AND NOT flag:trashed" "Last 7 days" ?7)
("flag:trashed" "Deleted messages" ?d)
("mime:image/*" "Messages with images" ?i)))
(message-send-mail-function . smtpmail-send-it)
(smtpmail-queue-dir . ,(concat mu4e-maildir maildir "/queue/cur"))
(smtpmail-smtp-user . ,email-address)
(smtpmail-starttls-credentials . (("smtp.gmail.com" 587 nil nil)))
(smtpmail-auth-credentials . (expand-file-name "~/.authinfo.gpg"))
(smtpmail-default-smtp-server . "smtp.gmail.com")
(smtpmail-smtp-server . "smtp.gmail.com")
(smtpmail-smtp-service . 587)
(smtpmail-debug-info . t)
(smtpmail-debug-verbose . t))))
Now let’s add the accounts:
(with-eval-after-load 'mu4e
(require 'smtpmail)
(setq smtpmail-queue-mail nil)
(setq mu4e-contexts
(list
(my/make-mu4e-context "personal" my/personal-email-address "/personal")
(my/make-mu4e-context "work" my/work-email-address "/work"))))
I want to be able to read PDF documents with pdf-tools
.
(use-package pdf-tools :ensure t :init (pdf-loader-install) :config (setq pdf-view-continuous nil))
I found Eshell smart display
on the Eshell article on Mastering
Emacs and I think it is pretty neat! It lets me review a failing
command and edit the command line to fix the error.
(require 'eshell)
(require 'em-smart)
(setq eshell-where-to-jump 'begin)
(setq eshell-review-quick-commands nil)
(setq eshell-smart-space-goes-to-end t)
Variable buffer-file-name
could be used together with a asynch
shell command.
Add work config where browse-url opens links in Chrome etc.
When the code is byte-compiled there are some stuff that is not found, might be worth investigating whether (eval-and-compile) works better.
This is very interesting, maybe something I might take advantage of myself?