diff options
| -rw-r--r-- | emacs/init.el | 3 | ||||
| -rw-r--r-- | emacs/init.html | 2163 | ||||
| -rw-r--r-- | emacs/init.org | 41 |
3 files changed, 2192 insertions, 15 deletions
diff --git a/emacs/init.el b/emacs/init.el index 589e48a..4129a5c 100644 --- a/emacs/init.el +++ b/emacs/init.el @@ -6,6 +6,9 @@ ;; We can't tangle without org! (require 'org) +;; Follow symlinks +(setq vc-follow-symlinks t) + ;; Open the configuration (find-file (concat user-emacs-directory "init.org")) diff --git a/emacs/init.html b/emacs/init.html new file mode 100644 index 0000000..c142d68 --- /dev/null +++ b/emacs/init.html @@ -0,0 +1,2163 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" +"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> +<head> +<title>mssola's GNU Emacs configuration</title> +<!-- 2017-01-14 Sat 15:56 --> +<meta http-equiv="Content-Type" content="text/html;charset=utf-8" /> +<meta name="generator" content="Org-mode" /> +<meta name="author" content="Miquel Sabaté Solà" /> +<style type="text/css"> + <!--/*--><![CDATA[/*><!--*/ + .title { text-align: center; } + .todo { font-family: monospace; color: red; } + .done { color: green; } + .tag { background-color: #eee; font-family: monospace; + padding: 2px; font-size: 80%; font-weight: normal; } + .timestamp { color: #bebebe; } + .timestamp-kwd { color: #5f9ea0; } + .right { margin-left: auto; margin-right: 0px; text-align: right; } + .left { margin-left: 0px; margin-right: auto; text-align: left; } + .center { margin-left: auto; margin-right: auto; text-align: center; } + .underline { text-decoration: underline; } + #postamble p, #preamble p { font-size: 90%; margin: .2em; } + p.verse { margin-left: 3%; } + pre { + border: 1px solid #ccc; + box-shadow: 3px 3px 3px #eee; + padding: 8pt; + font-family: monospace; + overflow: auto; + margin: 1.2em; + } + pre.src { + position: relative; + overflow: visible; + padding-top: 1.2em; + } + pre.src:before { + display: none; + position: absolute; + background-color: white; + top: -10px; + right: 10px; + padding: 3px; + border: 1px solid black; + } + pre.src:hover:before { display: inline;} + pre.src-sh:before { content: 'sh'; } + pre.src-bash:before { content: 'sh'; } + pre.src-emacs-lisp:before { content: 'Emacs Lisp'; } + pre.src-R:before { content: 'R'; } + pre.src-perl:before { content: 'Perl'; } + pre.src-java:before { content: 'Java'; } + pre.src-sql:before { content: 'SQL'; } + + table { border-collapse:collapse; } + caption.t-above { caption-side: top; } + caption.t-bottom { caption-side: bottom; } + td, th { vertical-align:top; } + th.right { text-align: center; } + th.left { text-align: center; } + th.center { text-align: center; } + td.right { text-align: right; } + td.left { text-align: left; } + td.center { text-align: center; } + dt { font-weight: bold; } + .footpara:nth-child(2) { display: inline; } + .footpara { display: block; } + .footdef { margin-bottom: 1em; } + .figure { padding: 1em; } + .figure p { text-align: center; } + .inlinetask { + padding: 10px; + border: 2px solid gray; + margin: 10px; + background: #ffffcc; + } + #org-div-home-and-up + { text-align: right; font-size: 70%; white-space: nowrap; } + textarea { overflow-x: auto; } + .linenr { font-size: smaller } + .code-highlighted { background-color: #ffff00; } + .org-info-js_info-navigation { border-style: none; } + #org-info-js_console-label + { font-size: 10px; font-weight: bold; white-space: nowrap; } + .org-info-js_search-highlight + { background-color: #ffff00; color: #000000; font-weight: bold; } + /*]]>*/--> +</style> +<script type="text/javascript"> +/* +@licstart The following is the entire license notice for the +JavaScript code in this tag. + +Copyright (C) 2012-2013 Free Software Foundation, Inc. + +The JavaScript code in this tag is free software: you can +redistribute it and/or modify it under the terms of the GNU +General Public License (GNU GPL) as published by the Free Software +Foundation, either version 3 of the License, or (at your option) +any later version. The code is distributed WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU GPL for more details. + +As additional permission under GNU GPL version 3 section 7, you +may distribute non-source (e.g., minimized or compacted) forms of +that code without the copy of the GNU GPL normally required by +section 4, provided you include this license notice and a URL +through which recipients can access the Corresponding Source. + + +@licend The above is the entire license notice +for the JavaScript code in this tag. +*/ +<!--/*--><![CDATA[/*><!--*/ + function CodeHighlightOn(elem, id) + { + var target = document.getElementById(id); + if(null != target) { + elem.cacheClassElem = elem.className; + elem.cacheClassTarget = target.className; + target.className = "code-highlighted"; + elem.className = "code-highlighted"; + } + } + function CodeHighlightOff(elem, id) + { + var target = document.getElementById(id); + if(elem.cacheClassElem) + elem.className = elem.cacheClassElem; + if(elem.cacheClassTarget) + target.className = elem.cacheClassTarget; + } +/*]]>*///--> +</script> +</head> +<body> +<div id="content"> +<h1 class="title">mssola's GNU Emacs configuration</h1> +<div id="table-of-contents"> +<h2>Table of Contents</h2> +<div id="text-table-of-contents"> +<ul> +<li><a href="#sec-1">1. Preface</a></li> +<li><a href="#sec-2">2. Introduction</a></li> +<li><a href="#sec-3">3. General</a> +<ul> +<li><a href="#sec-3-1">3.1. GUI</a></li> +<li><a href="#sec-3-2">3.2. Basic editing configuration</a></li> +<li><a href="#sec-3-3">3.3. Font and theme</a></li> +<li><a href="#sec-3-4">3.4. General global key bindings</a></li> +<li><a href="#sec-3-5">3.5. Others</a></li> +</ul> +</li> +<li><a href="#sec-4">4. Calendar</a></li> +<li><a href="#sec-5">5. General purpose defuns</a></li> +<li><a href="#sec-6">6. Lisp packages</a> +<ul> +<li><a href="#sec-6-1">6.1. Custom packages</a></li> +<li><a href="#sec-6-2">6.2. use-package</a></li> +</ul> +</li> +<li><a href="#sec-7">7. Project</a></li> +<li><a href="#sec-8">8. Edit</a></li> +<li><a href="#sec-9">9. Dired</a></li> +<li><a href="#sec-10">10. Evil</a></li> +<li><a href="#sec-11">11. Magit</a></li> +<li><a href="#sec-12">12. mu4e</a></li> +<li><a href="#sec-13">13. org</a> +<ul> +<li><a href="#sec-13-1">13.1. <span class="todo TODO">TODO</span> shortcut for making an org link, and transforming a link into a proper org link</a></li> +<li><a href="#sec-13-2">13.2. <span class="todo TODO">TODO</span> make it work with evil</a></li> +<li><a href="#sec-13-3">13.3. <span class="todo TODO">TODO</span> Proper keybindings for quick access.</a></li> +</ul> +</li> +<li><a href="#sec-14">14. Languages</a></li> +<li><a href="#sec-15">15. Misc</a></li> +</ul> +</div> +</div> + +<div id="outline-container-sec-1" class="outline-2"> +<h2 id="sec-1"><span class="section-number-2">1</span> Preface</h2> +<div class="outline-text-2" id="text-1"> +<p> +<a id="sec:preface" name="sec:preface"></a> +</p> + +<p> +This is my attempt of getting my configuration organized with literate +programming and <code>org-babel</code>. I took the workflow I'm following from <a href="https://github.com/larstvei">@larstvei</a>. +The idea is that the <code>init.el</code> file will replace itself on the first execution +with the tangled version of this file. This first version will also be +byte-compiled. This means that all changes are to be made on the <code>init.org</code> +file <b>always</b>. The initial contents of the <code>init.el</code> are: +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">;; We can't tangle without org! +(require 'org) + +;; Follow symlinks +(setq vc-follow-symlinks t) + +;; Open the configuration +(find-file (concat user-emacs-directory "init.org")) + +;; Tangle it +(org-babel-tangle) + +;; Load it +(load-file (concat user-emacs-directory "init.el")) + +;; Finally byte-compile it +(byte-compile-file (concat user-emacs-directory "init.el")) +</pre> +</div> + +<p> +There is no reason to track the init.el file, and no reason to change this +file. In order to ensure this, make sure to apply the following command after +cloning: +</p> + +<div class="org-src-container"> + +<pre class="src src-sh">git update-index --assume-unchanged emacs/init.el +</pre> +</div> + +<p> +Moreover, in order to avoid manually tangling and compiling the <code>init.org</code> file +on each change, the following function is provided: +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun tangle-init () + "If the current buffer is 'init.org' the code-blocks are + tangled, and the tangled file is compiled. Morever, an init.html is generated." + + (when (or (equal (buffer-file-name) + (expand-file-name (concat user-emacs-directory "init.org"))) + (string-suffix-p "dotfiles/emacs/init.org" (buffer-file-name))) + + ;; Avoid running hooks when tangling + (let ((prog-mode-hook nil)) + (org-babel-tangle) + (org-html-export-to-html) + (byte-compile-file (concat user-emacs-directory "init.el"))))) + +(add-hook 'after-save-hook 'tangle-init) +</pre> +</div> +</div> +</div> +<div id="outline-container-sec-2" class="outline-2"> +<h2 id="sec-2"><span class="section-number-2">2</span> Introduction</h2> +<div class="outline-text-2" id="text-2"> +<p> +I'm using GNU Emacs 25, but everything should be working in GNU Emacs 24. There +are some exceptions to this (e.g. webkit support), but these cases are +individually handled. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(unless (>= emacs-major-version 24) + (error "Don't be a cheap bastard and upgrade to at least GNU Emacs 24")) +</pre> +</div> + +<p> +Temporarily reduce garbage collection so startup time is lower. Idea taken from +<a href="https://github.com/purcell">@purcell</a>. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defconst mssola-initial-gc-cons-threshold gc-cons-threshold + "Initial value of `gc-cons-threshold' at start-up time.") +(setq gc-cons-threshold (* 1024 1024 1024)) +(add-hook 'after-init-hook + (lambda () (setq gc-cons-threshold mssola-initial-gc-cons-threshold))) +</pre> +</div> + +<p> +User name and email. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq user-full-name "Miquel Sabaté Solà" + user-mail-address "mikisabate@gmail.com") +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-3" class="outline-2"> +<h2 id="sec-3"><span class="section-number-2">3</span> General</h2> +<div class="outline-text-2" id="text-3"> +</div><div id="outline-container-sec-3-1" class="outline-3"> +<h3 id="sec-3-1"><span class="section-number-3">3.1</span> GUI</h3> +<div class="outline-text-3" id="text-3-1"> +<p> +I like a minimalistic GUI. Because of this, almost all GUI elements have been +disabled or tweaked in some custom way. +</p> + +<p> +The frame title is "<login>: <path>". If we are not editing a file, then the +name of the buffer is displayed (e.g. "mssola: <b>scratch</b>"). +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq frame-title-format + '((:eval + (concat (user-real-login-name) ": " + (if (buffer-file-name) + (abbreviate-file-name (buffer-file-name)) + "%b"))))) +</pre> +</div> + +<p> +Disable the menu, scroll and tool bars. At the same time, enable line and column +modes. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(menu-bar-mode -1) +(when (fboundp 'set-scroll-bar-mode) + (set-scroll-bar-mode nil)) +(when (fboundp 'tool-bar-mode) + (tool-bar-mode -1)) +(when (fboundp 'tooltip-mode) + (tooltip-mode 0)) + +(line-number-mode 1) +(column-number-mode 1) + +;; Nice scrolling +(setq scroll-margin 0 + scroll-conservatively 100000 + scroll-preserve-screen-position 1) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-3-2" class="outline-3"> +<h3 id="sec-3-2"><span class="section-number-3">3.2</span> Basic editing configuration</h3> +<div class="outline-text-3" id="text-3-2"> +<p> +Use UTF-8 <b>always</b>. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(prefer-coding-system 'utf-8) +(set-default-coding-systems 'utf-8) +(set-terminal-coding-system 'utf-8) +(set-keyboard-coding-system 'utf-8) +(set-language-environment 'utf-8) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-3-3" class="outline-3"> +<h3 id="sec-3-3"><span class="section-number-3">3.3</span> Font and theme</h3> +<div class="outline-text-3" id="text-3-3"> +<p> +I'm using "Droid Sans Mono" simply because I've grown used to it. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(set-frame-font "Droid Sans Mono Dotted for Powerline-10") +(add-to-list 'default-frame-alist '(font . "Droid Sans Mono Dotted for Powerline-10")) + +; Emacs in daemon mode does not like `set-face-attribute` because this is only +; applied if there is a frame in place, which doesn't happen when starting the +; daemon. Thus, we should call that after the frame has been created (e.g. by +; emacsclient). +; See: https://lists.gnu.org/archive/html/help-gnu-emacs/2015-03/msg00016.html +(add-hook 'after-make-frame-functions-hook + (lambda () + (set-face-attribute 'default t :font "Droid Sans Mono Dotted for Powerline-10"))) +</pre> +</div> + +<p> +I've hacked my own theme called <a href="https://github.com/mssola/soria">soria</a>. This theme combines the vim theme +<a href="http://www.vim.org/scripts/script.php?script_id=2140">xoria256</a> with the <a href="http://opensuse.github.io/branding-guidelines/">openSUSE branding guidelines</a>. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(load-theme 'soria t) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-3-4" class="outline-3"> +<h3 id="sec-3-4"><span class="section-number-3">3.4</span> General global key bindings</h3> +<div class="outline-text-3" id="text-3-4"> +<p> +Use kill-this-buffer instead of kill-buffer. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(global-set-key (kbd "C-x k") 'kill-this-buffer) +</pre> +</div> + +<p> +Disable C-z. It will later on be picked up by Evil's config as the escape +sequence. This is here to make sure that it will be disabled even if Evil +cannot be loaded due to some error. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(global-unset-key (kbd "C-z")) +</pre> +</div> + +<p> +Disable all the Fn keys. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(dotimes (i 12) + (global-unset-key (kbd (format "<f%d>" (+ i 1))))) +</pre> +</div> + +<p> +Disable overwrite-mode. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(define-key global-map [(insert)] nil) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-3-5" class="outline-3"> +<h3 id="sec-3-5"><span class="section-number-3">3.5</span> Others</h3> +<div class="outline-text-3" id="text-3-5"> +<p> +Revert buffers automatically when underlying files are changed externally. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(global-auto-revert-mode t) +</pre> +</div> + +<p> +Follow symlinks. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq vc-follow-symlinks t) +</pre> +</div> + +<p> +Remove the initial message from the scratch buffer. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq initial-scratch-message nil) +</pre> +</div> + +<p> +Never kill the scratch buffer, bury it instead. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defadvice kill-buffer (around kill-buffer-around-advice activate) + (let ((buffer-to-kill (ad-get-arg 0))) + (if (equal buffer-to-kill "*scratch*") + (bury-buffer) + ad-do-it))) +</pre> +</div> + +<p> +No backups +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq-default make-backup-files nil) +(setq-default auto-save-default nil) +</pre> +</div> + +<p> +No welcome screen +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq-default inhibit-startup-message t) +</pre> +</div> + +<p> +Enable y/n answers +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(fset 'yes-or-no-p 'y-or-n-p) +</pre> +</div> + +<p> +Let flyspell be performant. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defvar flyspell-issue-message-flag nil) +</pre> +</div> + +<p> +Save custom-variables somewhere else. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq custom-file (expand-file-name "custom.el" user-emacs-directory)) +(if (file-exists-p custom-file) + (load custom-file)) +</pre> +</div> +</div> +</div> +</div> + +<div id="outline-container-sec-4" class="outline-2"> +<h2 id="sec-4"><span class="section-number-2">4</span> Calendar</h2> +<div class="outline-text-2" id="text-4"> +<p> +We catalans start our weeks on Monday. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defvar calendar-week-start-day 1) +</pre> +</div> + +<p> +Global key binding. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(global-set-key (kbd "C-c c") 'calendar) +</pre> +</div> + +<p> +Fix some stuff for evil mode. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(with-eval-after-load "evil" + (evil-set-initial-state 'calendar-mode 'normal) + (evil-define-key 'normal calendar-mode-map + "j" 'calendar-forward-week + "k" 'calendar-backward-week + "b" 'calendar-backward-day + "h" 'calendar-backward-day + "l" 'calendar-forward-day + "w" 'calendar-forward-day + "q" 'calendar-exit + "\C-h" 'evil-window-left + "\C-l" 'evil-window-right + "\C-j" 'evil-window-down + "\C-k" 'evil-window-up + "\C-n" 'calendar-scroll-left-three-months + "\C-p" 'calendar-scroll-right-three-months)) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-5" class="outline-2"> +<h2 id="sec-5"><span class="section-number-2">5</span> General purpose defuns</h2> +<div class="outline-text-2" id="text-5"> +<p> +I want to read the latest news. That's why I define a function that downloads +the <code>NEWS</code> file from the git server and then opens it in a buffer. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun mssola-view-emacs-latest-news () + "Allow users to fetch the latest Emacs' NEWS file." + (interactive) + + (url-copy-file + "http://git.savannah.gnu.org/cgit/emacs.git/plain/etc/NEWS" + "/tmp/emacs-news" t) + + (find-file-read-only "/tmp/emacs-news" t)) +</pre> +</div> + +<p> +Sometimes I want to debug my initialization time. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun emacs-init-time () + "Redefine the `emacs-init-time' function so it is more detailed. +Idea taken from @purcell." + + (interactive) + (let ((init-time + (float-time (time-subtract after-init-time before-init-time)))) + (message "%.3fs" init-time))) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-6" class="outline-2"> +<h2 id="sec-6"><span class="section-number-2">6</span> Lisp packages</h2> +<div class="outline-text-2" id="text-6"> +</div><div id="outline-container-sec-6-1" class="outline-3"> +<h3 id="sec-6-1"><span class="section-number-3">6.1</span> Custom packages</h3> +<div class="outline-text-3" id="text-6-1"> +<p> +Compile the <code>g.el</code> script and bind it to <kbd>M-g</kbd>. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(byte-compile-file (concat user-emacs-directory "lisp/g.el") t) +(global-set-key (kbd "M-g") 'g) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-6-2" class="outline-3"> +<h3 id="sec-6-2"><span class="section-number-3">6.2</span> use-package</h3> +<div class="outline-text-3" id="text-6-2"> +<p> +Initialize package. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(require 'package) + +(add-to-list 'package-archives + '("melpa" . "http://melpa.milkbox.net/packages/") t) +(add-to-list 'package-archives + '("melpa-stable" . "https://stable.melpa.org/packages/") t) + +(package-initialize) +</pre> +</div> + +<p> +I'm using use-package to handle my installed packages. I don't know if it's +the best option or what because I haven't tested all the package managers +for Emacs out there. After trying some custom functions to handle +package-install, I decided on use-package because I feel more well-organized. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(unless (package-installed-p 'use-package) + (package-refresh-contents) + (package-install 'use-package)) +</pre> +</div> +</div> +</div> +</div> + +<div id="outline-container-sec-7" class="outline-2"> +<h2 id="sec-7"><span class="section-number-2">7</span> Project</h2> +<div class="outline-text-2" id="text-7"> +<p> +First of all, load the silver searcher, which is a convenient and fast searcher. +Ayo silver! +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package ag + :ensure t + :config + + ; Avoid some disagreements between ag and evil. + (with-eval-after-load 'evil + (add-hook 'ag-mode-hook + (lambda () + (define-key ag-mode-map (kbd "n") 'evil-search-next) + (define-key ag-mode-map (kbd "N") 'evil-search-previous) + (define-key ag-mode-map (kbd "gg") 'evil-goto-first-line)))) + (setq ag-reuse-buffers t) + (setq ag-reuse-window t)) +</pre> +</div> + +<p> +Then, for keeping up with my projects I use the Projectile + Helm combination. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package projectile + :ensure t + :config + (projectile-mode 1) + (setq projectile-mode-line + '(:eval (format " %s" (projectile-project-name))))) + +(use-package helm + :ensure t + :config + (setq projectile-completion-system 'helm) + + ; Allow the search pattern to be on the header. Taken from this Reddit thread: + ; https://www.reddit.com/r/emacs/comments/3asbyn/new_and_very_useful_helm_feature_enter_search/ + (setq helm-echo-input-in-header-line t) + + (defun helm-hide-minibuffer-maybe () + "Hide the minibuffer if we are in a Helm session" + + (when (with-helm-buffer helm-echo-input-in-header-line) + (let ((ov (make-overlay (point-min) (point-max) nil nil t))) + (overlay-put ov 'window (selected-window)) + (overlay-put ov 'face (let ((bg-color (face-background 'default nil))) + `(:background ,bg-color :foreground ,bg-color))) + (setq-local cursor-type nil)))) + + (add-hook 'helm-minibuffer-set-up-hook 'helm-hide-minibuffer-maybe) + (setq helm-split-window-in-side-p t) + + ; Preview files with tab + (define-key helm-map (kbd "<tab>") 'helm-execute-persistent-action) + + ; Show available options + (define-key helm-map (kbd "C-a") 'helm-select-action) + + ; Some vim-like bindings + (define-key helm-map (kbd "C-j") 'helm-next-line) + (define-key helm-map (kbd "C-k") 'helm-previous-line) + + (use-package helm-ag + :ensure t)) + +(use-package helm-projectile + :ensure t + :config + (helm-projectile-on)) +</pre> +</div> + +<p> +Finally, I've added a couple of bindings in evil mode. First of all, I want +<kbd>C-p</kbd> to use the proper function for finding files. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun mssola-find-file () + "Call the proper Helm function for finding files." + + (interactive) + + (condition-case nil + (helm-projectile-find-file) + (error + (helm-find-files nil)))) + +(with-eval-after-load 'evil + (define-key evil-normal-state-map (kbd "C-p") 'mssola-find-file)) +</pre> +</div> + +<p> +Similarly, <code>helm-ag</code> has two functions for applying <code>ag</code> on the project. I'm +binding to <kbd>,a</kbd> a function that calls to the proper function. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun mssola-helm-ag () + "Call the right ag command for helm-ag." + + (interactive) + + (condition-case nil + (helm-ag-project-root) + (error (helm-ag)))) + +(with-eval-after-load 'evil-leader + (evil-leader/set-key "a" 'mssola-helm-ag)) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-8" class="outline-2"> +<h2 id="sec-8"><span class="section-number-2">8</span> Edit</h2> +<div class="outline-text-2" id="text-8"> +<p> +In this section I define some useful packages for editing. First of all, one of +the coolest packages out there is <code>undo-tree</code>. It allows you to navigate through +the undo history in a tree (because GNU Emacs is cool and keeps track of undo +actions in a tree structure instead of in a stack). This package is included in +recent versions of GNU Emacs. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(with-eval-after-load 'undo-tree + (global-undo-tree-mode 1) + (setq undo-tree-visualizer-diff t) + (setq undo-tree-visualizer-timestamps t) + (with-eval-after-load 'evil-leader + (evil-leader/set-key + "u" 'undo-tree-visualize))) +</pre> +</div> + +<p> +Another important package is <code>flycheck</code>, which is a on-the-fly syntax checking +extension. This works with lots of languages with proper glue code. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package let-alist + :ensure t) + +(use-package flycheck + :ensure t + :config + (add-hook 'after-init-hook 'global-flycheck-mode) + + ;; Only show the errors buffer if it isn't there and if I'm saving the + ;; buffer. + (setq flycheck-emacs-lisp-load-path 'inherit) + (setq flycheck-check-syntax-automatically '(mode-enabled save)) + (setq flycheck-display-errors-function + #'flycheck-display-error-messages-unless-error-list)) +</pre> +</div> + +<p> +A recurring issue in speeches and presentations is that when showing something +with your editor, you have to increase/decrease the fonts. I use the +<code>default-text-scale</code> package for this. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package default-text-scale + :ensure t + :config + (global-set-key (kbd "C-+") 'default-text-scale-increase) + (global-set-key (kbd "C--") 'default-text-scale-decrease)) +</pre> +</div> + +<p> +Some languages use some delimiters a lot (e.g. lisp languages and +parenthesis). For this reason I'm using the <code>rainbow-delimiters</code> package, which +properly highlights each level in a different way (provided that your theme +supports it). +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package rainbow-delimiters + :ensure t) +</pre> +</div> + +<p> +I never use the mouse. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package disable-mouse + :ensure t + :config + (global-disable-mouse-mode)) +</pre> +</div> + +<p> +Sometimes you begin typing a prefix, but then you forget the following +chord. For this reason <code>which-key</code> was created. It will show the available +commands for the current chord as a list. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package which-key + :ensure t) +</pre> +</div> + +<p> +For some modes it is important to count the number of words in the text. For +this, we have <code>wc-mode</code>. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package wc-mode + :ensure t) +</pre> +</div> + +<p> +Next on the list, we have <code>writeroom-mode</code>. I honestly don't use this package +that often, but it's cool to have a distraction-free screen from time to time. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package writeroom-mode + :ensure t) +</pre> +</div> + +<p> +Finally, we have <code>expand-region</code>. This is an extension that is based on a Vim +one, which expands the region of selection with a single key chord. Even if evil +covers most of my uses, it's convenient to have this tool anyways. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package expand-region + :ensure t + :config + + ; Set C-e as the expand command (mnemonic: expand). This command will + ; supercede the evil binding for "move one line", which I never use anyways. + (global-set-key (kbd "\C-e") 'er/expand-region) + (with-eval-after-load 'evil + (define-key evil-normal-state-map (kbd "\C-e") 'er/expand-region) + (define-key evil-visual-state-map (kbd "\C-e") 'er/expand-region))) + +(defun er/add-text-mode-expansions () + "This way we can also expand the region into paragraphs & pages in text mode. +Directly taken from: https://github.com/magnars/expand-region.el." + + (make-variable-buffer-local 'er/try-expand-list) + (setq er/try-expand-list (append + er/try-expand-list + '(mark-paragraph + mark-page)))) + +(add-hook 'text-mode-hook 'er/add-text-mode-expansions) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-9" class="outline-2"> +<h2 id="sec-9"><span class="section-number-2">9</span> Dired</h2> +<div class="outline-text-2" id="text-9"> +<p> +I use dired mode mainly for attaching document into emails. That being said, +whenever I use it, I want basic evil movement. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(with-eval-after-load 'evil + (evil-add-hjkl-bindings dired-mode-map 'normal + (kbd "w") 'evil-forward-word-begin)) +</pre> +</div> + +<p> +And now instruct dired mode how to attach files when using mu4e. This is taken +from the <a href="https://www.djcbsoftware.nl/code/mu/mu4e/Dired.html#Dired">mu4e documentation</a>, and it's available by typing +<kbd>C-c</kbd><kbd>RET</kbd><kbd>C-a</kbd>. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(require 'gnus-dired) + +;; Make the `gnus-dired-mail-buffers' function also work on message-mode derived +;; modes, such as mu4e-compose-mode. +(defun gnus-dired-mail-buffers () + "Return a list of active message buffers." + + (let (buffers) + (save-current-buffer + (dolist (buffer (buffer-list t)) + (set-buffer buffer) + (when (and (derived-mode-p 'message-mode) + (null message-sent-message-via)) + (push (buffer-name buffer) buffers)))) + (nreverse buffers))) + +(setq gnus-dired-mail-mode 'mu4e-user-agent) +(add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-10" class="outline-2"> +<h2 id="sec-10"><span class="section-number-2">10</span> Evil</h2> +<div class="outline-text-2" id="text-10"> +<p> +Forgive me, <a href="https://stallman.org/saint.html">Father</a>, for I have sinned. As I said in the <i>Preface</i>, I've been +exposed to modal editing through Vim, and that has changed how I view editing +for the foreseeable future. Because of this, I use Evil. The following block +includes some heavy-lifting so Evil and GNU Emacs work without hitting each +other, and it also includes some Evil extensions. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun mssola-evil () + "Configure evil mode." + + ; We can safely remap <C-u> because the counting will be handled a-la Vim. + (define-key evil-normal-state-map (kbd "C-u") 'evil-scroll-up) + + ; Make window navigation easier. + (define-key evil-normal-state-map (kbd "C-j") 'evil-window-down) + (define-key evil-normal-state-map (kbd "C-k") 'evil-window-up) + (define-key evil-normal-state-map (kbd "C-l") 'evil-window-right) + (define-key evil-normal-state-map (kbd "C-h") 'evil-window-left) + + ; The window navigation tweaks effectively wipe out the help prefix, which + ; is bad. Fortunately we can workaround this by providing "M-h" as the new + ; help prefix. This prefix is only used in emacs mode to mark lines, which is + ; something already handled by Evil. + (define-key global-map (kbd "M-h") 'help-command) + (fset 'help-command help-map) + + ; Helm + Projectile shortcuts. + (with-eval-after-load 'helm-projectile + (define-key evil-normal-state-map (kbd "M-p") + 'helm-projectile-switch-project)) + + ; I use the Super key in combination with j & k to move around i3. Let's unset + ; M- combos for these two fellows for whenever I misstype them. + (global-unset-key (kbd "M-j")) + (global-unset-key (kbd "M-k")) + + ; both this function and the subsequent lines about [escape] are taken from + ; @aaronbieber configuration. + (defun minibuffer-keyboard-quit () + "Abort recursive edit. +In Delete Selection mode, if the mark is active, just deactivate it; +then it takes a second \\[keyboard-quit] to abort the minibuffer." + (interactive) + (if (and delete-selection-mode transient-mark-mode mark-active) + (setq deactivate-mark t) + (when (get-buffer "*Completions*") (delete-windows-on "*Completions*")) + (abort-recursive-edit))) + + ;; Make escape quit everything, whenever possible. + (define-key evil-normal-state-map [escape] 'keyboard-quit) + (define-key evil-visual-state-map [escape] 'keyboard-quit) + (define-key minibuffer-local-map [escape] 'minibuffer-keyboard-quit) + (define-key minibuffer-local-ns-map [escape] 'minibuffer-keyboard-quit) + (define-key minibuffer-local-completion-map [escape] 'minibuffer-keyboard-quit) + (define-key minibuffer-local-must-match-map [escape] 'minibuffer-keyboard-quit) + (define-key minibuffer-local-isearch-map [escape] 'minibuffer-keyboard-quit) + + ; I store macros on the <q> register for convenience, so I used to use the + ; <C-q> combo to execute this macro in Vim. In Emacs though, this combo is + ; reserved to a rather useful function, and I'd like to keep it that way. So, + ; now the mapping is set to <C-2> (mnemonic: where the @ symbol is). Moreover, + ; it's applied as many times as specified by the numeric prefix argument. + (define-key evil-normal-state-map (kbd "C-2") + (lambda (n) + (interactive "p") + (evil-execute-macro n "@q"))) + + ; C-s: switch to normal mode and save the buffer. I know :) + (define-key evil-normal-state-map (kbd "C-s") 'save-buffer) + (define-key evil-insert-state-map (kbd "C-s") + (lambda () (interactive) (save-buffer) (evil-force-normal-state)))) + +(use-package evil + :ensure t + :config + + (add-hook 'evil-mode-hook 'mssola-evil) + (evil-mode 1) + + ;; C-z is unused and it's close to my beloved C-c. Since I don't want to mess + ;; with one of the most sacred Emacs prefixes, I'm moving to C-z. + (define-key key-translation-map (kbd "C-z") [escape]) + (define-key evil-operator-state-map (kbd "C-z") 'keyboard-quit) + (set-quit-char "C-z") + + ;; Use the proper initial evil state for the following modes. + (evil-set-initial-state 'help-mode 'normal) + (evil-set-initial-state 'debugger-mode 'normal) + (evil-set-initial-state 'describe-mode 'normal) + (evil-set-initial-state 'Buffer-menu-mode 'normal) + + (use-package evil-leader + :ensure t + :config + (global-evil-leader-mode) + (evil-leader/set-leader ",") + (setq evil-leader/in-all-states 1) + (evil-leader/set-key + "," 'back-to-indentation + "c" 'delete-window + "k" 'kill-buffer-and-window + "v" 'split-window-right + "V" (lambda () (interactive) (split-window-right) (other-window 1)) + "f" 'flycheck-list-errors + "e" 'eval-last-sexp + "b" 'view-buffer + "o" 'browse-url-at-point)) + + (use-package evil-surround + :ensure t + :config + (global-evil-surround-mode 1)) + + (use-package evil-commentary + :ensure t + :config + (evil-commentary-mode t)) + + (use-package evil-args + :ensure t + :config + ; Configuration taken from the documentation of evil-args. + + ;; Bind evil-args text objects + (define-key evil-inner-text-objects-map "a" 'evil-inner-arg) + (define-key evil-outer-text-objects-map "a" 'evil-outer-arg) + + ;; Bind evil-forward/backward-args + (define-key evil-normal-state-map "L" 'evil-forward-arg) + (define-key evil-normal-state-map "H" 'evil-backward-arg) + (define-key evil-motion-state-map "L" 'evil-forward-arg) + (define-key evil-motion-state-map "H" 'evil-backward-arg)) + + (use-package evil-numbers + :ensure t + :config + (define-key evil-normal-state-map (kbd "C-a") 'evil-numbers/inc-at-pt) + (define-key evil-normal-state-map (kbd "C-c -") 'evil-numbers/dec-at-pt))) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-11" class="outline-2"> +<h2 id="sec-11"><span class="section-number-2">11</span> Magit</h2> +<div class="outline-text-2" id="text-11"> +<p> +A git porcelain for GNU Emacs. Even if I'm still using the git CLI, it's +certainly useful for some common tasks (I guess that I still need some learning). +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package magit + :ensure t + :config + + ; Proper initial states. + (with-eval-after-load 'evil + (add-hook 'git-commit-mode-hook 'evil-insert-state) + (evil-set-initial-state 'magit-log-edit-mode 'insert)) + + ; When showing the status, hide the usually-redundant "branch" section and + ; show the rest. + (add-hook 'magit-section-set-visibility-hook + '(lambda (section) + (if (string= (magit-section-type section) "branch") + 'hide + 'show))) + + (with-eval-after-load 'evil-leader + (evil-leader/set-key "s" 'magit-status) + + (use-package evil-magit + :ensure t + :config + + ; The magit + evil-magit combo messes up some chords, let's fix this. + (evil-define-key 'normal magit-mode-map + "\C-h" 'evil-window-left + "\C-l" 'evil-window-right + "\C-j" 'evil-window-down + "\C-k" 'evil-window-up + "\M-p" 'helm-projectile-switch-project)))) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-12" class="outline-2"> +<h2 id="sec-12"><span class="section-number-2">12</span> mu4e</h2> +<div class="outline-text-2" id="text-12"> +<p> +I use <a href="http://www.djcbsoftware.nl/code/mu/">mu</a> and <a href="http://www.djcbsoftware.nl/code/mu/mu4e.html">mu4e</a> to manage my email. The configuration for this has been taken +mainly from the documentation, plus some cool remarks on Reddit. This +configuration makes quite some assumptions. Read the <code>emacs/README.org</code> file as +provided in my <code>dotfiles</code> project to get more details. +</p> + +<p> +I'm using an RPM that I've built on <a href="https://build.opensuse.org/package/show/home:mssola/mu">OBS</a> which installs mu4e globally. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e") +(require 'mu4e) + +(when (featurep 'mu4e) +</pre> +</div> + +<p> +Diferent SMTP options that will be used for each context. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq message-send-mail-function 'smtpmail-send-it + mu4e-maildir (expand-file-name "~/.mail") + starttls-use-gnutls t) +</pre> +</div> + +<p> +After that, I am defining some functions that will be used in various parts of +the configuration. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun mssola-smtp (server port) + "Set SMTP variables depending on the given SERVER and PORT." + + (require 'smtpmail) + + (setq smtpmail-starttls-credentials + '((server port nil nil)) + smtpmail-auth-credentials + (expand-file-name "~/.authinfo.gpg") + smtpmail-default-smtp-server server + smtpmail-smtp-server server + smtpmail-smtp-service port)) + +;; Define the different accounts that I'm using. This is only available since +;; mu 0.9.16. + +; https://www.reddit.com/r/emacs/comments/47t9ec/share_your_mu4econtext_configs/d0fsih6 +(defun mu4e-message-maildir-matches (msg rx) + (when rx + (if (listp rx) + ;; if rx is a list, try each one for a match + (or (mu4e-message-maildir-matches msg (car rx)) + (mu4e-message-maildir-matches msg (cdr rx))) + ;; not a list, check rx + (string-match rx (mu4e-message-field msg :maildir))))) + +(defun suse-refile-folder (key) + "Returns the refile folder for the given SUSE account in the KEY arg" + (concat "/" key "/Archives/" + (format-time-string "%Y" (current-time)))) +</pre> +</div> + +<p> +Now it's time to define the different contexts that I have. Defining contexts +this way is relatively new (since mu 0.16). +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq mu4e-contexts + `( + ;; GMail + ,(make-mu4e-context + :name "gmail" + :enter-func (lambda () + (mu4e-message "Switching to gmail.com") + (mssola-smtp "smtp.gmail.com" 587)) + :match-func (lambda (msg) + (when msg + (mu4e-message-maildir-matches msg "^/gmail"))) + :vars '( + (user-mail-address . "mikisabate@gmail.com") + (mu4e-reply-to-address . "mikisabate@gmail.com") + (mu4e-drafts-folder . "/gmail/Drafts") + (mu4e-sent-folder . "/gmail/Sent") + (mu4e-refile-folder . "/gmail/All") + (mu4e-trash-folder . "/gmail/Trash"))) + + ;; suse.com + ,(make-mu4e-context + :name "comsuse" + :enter-func (lambda () + (mu4e-message "Switching to suse.com") + (mssola-smtp "smtp.novell.com" 25)) + :match-func (lambda (msg) + (when msg + (mu4e-message-maildir-matches msg "^/susecom"))) + :vars `( + (user-mail-address . "msabate@suse.com") + (mu4e-reply-to-address . "msabate@suse.com") + (mu4e-drafts-folder . "/susecom/Drafts") + (mu4e-sent-folder . "/susecom/Sent") + (mu4e-refile-folder . ,(suse-refile-folder "susecom")) + (mu4e-trash-folder . "/susecom/Trash"))) + + ;; suse.de + ,(make-mu4e-context + :name "desuse" + :enter-func (lambda () + (mu4e-message "Switching to suse.de") + (mssola-smtp "imap.suse.de" 587)) + :match-func (lambda (msg) + (when msg + (mu4e-message-maildir-matches msg "^/susede"))) + :vars `( + (user-mail-address . "msabate@suse.de") + (mu4e-reply-to-address . "msabate@suse.de") + (mu4e-drafts-folder . "/susede/Drafts") + (mu4e-sent-folder . "/susede/Sent") + (mu4e-refile-folder . ,(suse-refile-folder "susede")) + (mu4e-trash-folder . "/susede/Trash"))))) +</pre> +</div> + +<p> +If mu4e cannot figure things out, ask me. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq mu4e-context-policy 'ask) +(setq mu4e-compose-context-policy 'ask) +</pre> +</div> + +<p> +Fill the <code>mu4e-user-mail-address-list</code> variable with the contexts. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq mu4e-user-mail-address-list + (delq nil + (mapcar (lambda (context) + (when (mu4e-context-vars context) + (cdr (assq 'user-mail-address + (mu4e-context-vars context))))) + mu4e-contexts))) +</pre> +</div> + +<p> +Setting my bookmarks +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq mu4e-bookmarks + '(("maildir:/gmail/inbox OR maildir:/susecom/inbox OR maildir:/susede/inbox" "Inbox Folders" ?n) + ("flag:unread AND NOT flag:trashed" "Unread messages" ?u) + ("date:today..now" "Today's messages" ?t))) +</pre> +</div> + +<p> +The following signature looks alright regardless of whether the client supports +format=flowed or not. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq mu4e-compose-signature + (concat + "Miquel Sabaté Solà,\n" + "PGP: 4096R / 1BA5 3C7A C93D CA2A CFDF DA97 96BE 8C6F D89D 6565\n")) +</pre> +</div> + +<p> +Sign outgoing emails always. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(add-hook 'message-send-hook 'mml-secure-message-sign-pgpmime) +</pre> +</div> + +<p> +To avoid UID clashes. See <a href="http://pragmaticemacs.com/emacs/fixing-duplicate-uid-errors-when-using-mbsync-and-mu4e/">this</a>. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq mu4e-change-filenames-when-moving t) +</pre> +</div> + +<p> +Miscellaneous settings. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq mu4e-html2text-command "w3m -T text/html" + mu4e-attachment-dir "~/Downloads" + mu4e-headers-date-format "%Y-%m-%d %H:%M" + message-citation-line-format "%N @ %Y-%m-%d %H:%M %Z:\n" + message-citation-line-function 'message-insert-formatted-citation-line + message-kill-buffer-on-exit t + mu4e-get-mail-command "mbsync -aqV" + mu4e-update-interval 600 + mu4e-compose-dont-reply-to-self t + mu4e-compose-format-flowed t + mu4e-headers-skip-duplicates t + mu4e-headers-include-related t + mu4e-headers-auto-update t) +</pre> +</div> + +<p> +The headers to show in the headers list a pair of a field and its width, +with `nil' meaning 'unlimited' (better only use that for the last field. +These are the defaults: +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq mu4e-headers-fields + '( (:date . 18) + (:mailing-list . 15) + (:from-or-to . 20) + (:subject . nil))) +</pre> +</div> + +<p> +Show images +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq mu4e-view-show-images t + mu4e-view-image-max-width 800) + +; Use imagemagick, if available +(when (fboundp 'imagemagick-register-types) + (imagemagick-register-types)) +</pre> +</div> + +<p> +As of 0.9.18 and GNU Emacs 25, the <code>mu4e-action-with-xwidget</code> can be used to +render an HTML message with Webkit. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(if (>= emacs-major-version 25) + (add-to-list 'mu4e-view-actions + '("webkit" . mu4e-action-view-with-xwidget))) +</pre> +</div> + +<p> +Look for mu4e-msg2pdf in the exec path. The reason for this is that that OBS +package installs mu's <code>toys</code> into the exec path, but <code>mu4e</code> doesn't really count +on it. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(let ((exec (locate-file "msg2pdf" exec-path exec-suffixes))) + (if exec + (setq mu4e-msg2pdf exec))) +</pre> +</div> + +<p> +Adding hooks for composing and viewing messages. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun mssola-compose-mode () + "My settings for message composition." + + ; If we are composing an email from scratch, it's more convenient to be in + ; insert mode. Otherwise start with normal mode. + (with-eval-after-load 'evil + (if mu4e-compose-parent-message + (evil-set-initial-state 'mu4e-compose-mode 'normal) + (evil-set-initial-state 'mu4e-compose-mode 'insert))) + + ; Guess hard newlines + (use-hard-newlines t 'guess) + + ; So it's easy to encrypt/decrypt emails. + (epa-mail-mode) + + ; Spellz + (flyspell-mode)) + +(add-hook 'mu4e-compose-mode-hook 'mssola-compose-mode) + +; I want to read messages in the format that the sender used. I'm also +; enabling epa-mail-mode, so it's easy to decrypt received emails. +(add-hook 'mu4e-view-mode-hook + (lambda () + (epa-mail-mode) + (visual-line-mode 1))) +</pre> +</div> + +<p> +I want desktop notifications when receiving email. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package mu4e-alert + :ensure t + :config + + ; Notify me for unread emails from my inbox. + (mu4e-alert-set-default-style 'libnotify) + (add-hook 'after-init-hook #'mu4e-alert-enable-notifications) + (add-hook 'after-init-hook #'mu4e-alert-enable-mode-line-display) + (setq mu4e-alert-interesting-mail-query + (concat + "(maildir:/gmail/inbox OR maildir:/susecom/inbox OR maildir:/susede/inbox) " + "AND flag:unread AND NOT flag:trashed")) + (setq mu4e-alert-email-notification-types '(count))) + +; Evil mode in mu4e +(with-eval-after-load 'evil + (use-package evil-mu4e + :ensure t + :config + + ; Idea taken from evil-mu4e.el + (defvar mssola-evil-mu4e-mode-map-bindings + `((,evil-mu4e-state mu4e-headers-mode-map "\C-u" evil-scroll-up) + (,evil-mu4e-state mu4e-main-mode-map "\C-u" evil-scroll-up) + (,evil-mu4e-state mu4e-view-mode-map "h" evil-backward-char))) + + (dolist (binding mssola-evil-mu4e-mode-map-bindings) + (evil-define-key + (nth 0 binding) (nth 1 binding) (nth 2 binding) (nth 3 binding))))) +</pre> +</div> + +<p> +And finally define a proper shortcut. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">; The trailing parenthesis closes the "(when (featurep 'mu4e)" statement from +; the very beginning. +(global-set-key (kbd "C-c m") 'mu4e)) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-13" class="outline-2"> +<h2 id="sec-13"><span class="section-number-2">13</span> org</h2> +<div class="outline-text-2" id="text-13"> +<p> +First of all, let me define some helper functions. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun mssola-org-skip-if-priority (priority &optional subtree) + "Skip an agenda item if it has a priority of PRIORITY. +PRIORITY may be one of the characters ?A, ?B, or ?C. +Skips the current entry unless SUBTREE is not nil. This function has been +copied from @aaronbieber." + + (let ((end (if subtree (save-excursion (org-end-of-subtree t)) + (save-excursion (progn (outline-next-heading) (1- (point)))))) + (pri-value (* 1000 (- org-lowest-priority priority))) + (pri-current (org-get-priority (thing-at-point 'line t)))) + (if (= pri-value pri-current) + end + nil))) + +(defun mssola-org-skip-if-not-closed-in-day (time &optional subtree) + "Skip entries that were not closed in the given TIME. +Skip the current entry unless SUBTREE is not nil, in which case skip +the entire subtree. Idea taken from @aaronbieber" + + (let ((end (if subtree (save-excursion (org-end-of-subtree t)) + (save-excursion (progn (outline-next-heading) (1- (point)))))) + (day-prefix (format-time-string "%Y-%m-%d" time))) + + (if (save-excursion + (and (re-search-forward org-closed-time-regexp end t) + (string= (substring (match-string-no-properties 1) 0 10) day-prefix))) + nil + end))) +</pre> +</div> + +<p> +Some general UI settings for org mode. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq org-src-fontify-natively t + org-src-tab-acts-natively t + org-confirm-babel-evaluate nil + org-edit-src-content-indentation 0) + +(setq org-todo-keywords + '((sequence "TODO(t)" "|" "DONE(d!)") + (sequence "IDEA(i)" "WORKING(w)" "|" "USED(u@/!)" "DISCARDED(x@/!)"))) + +(setq org-todo-keyword-faces + '(("TODO" . org-todo) + ("IDEA" . font-lock-constant-face) + ("WORKING" . font-lock-constant-face) + ("DONE" . org-done) + ("USED" . org-done) + ("DISCARDED" . org-done))) +</pre> +</div> + +<p> +Loggins settings. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq org-log-done t) +(setq org-log-redeadline (quote time)) +(setq org-log-reschedule (quote time)) +</pre> +</div> + +<p> +Where org files reside. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq org-agenda-files '("~/org/")) +</pre> +</div> + +<p> +Custom commands for <code>org-agenda</code>. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq org-agenda-custom-commands + '(("p" "Printed agenda" + ; Daily agenda with a 2-weeks deadline warning. Tasks are + ; represented as [ ] items. + ((agenda "" + ((org-agenda-ndays 1) + (org-deadline-warning-days 14) + (org-agenda-todo-keyword-format "[ ]") + (org-agenda-scheduled-leaders '("" "")))) + + ; Display a "High Priority" list of tasks on top. + (tags "PRIORITY=\"A\"" + ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done)) + (org-agenda-sorting-strategy '(tag-up priority-down)) + (org-agenda-todo-keyword-format "") + (org-agenda-overriding-header "\nHigh priority\n--------------\n"))) + + + ; All tasks except those already listed as high priority or + ; ideas. Scheduled and deadlines are also ignored here. + (alltodo "" + ((org-agenda-skip-function '(or (mssola-org-skip-if-priority ?A) + (org-agenda-skip-entry-if 'todo '("IDEA" "WORKING")) + (org-agenda-skip-if nil '(scheduled deadline)))) + (org-agenda-sorting-strategy '(tag-up priority-down)) + (org-agenda-todo-keyword-format "") + (org-agenda-overriding-header "\nAll tasks\n----------\n"))) + + ; List of ideas. + (todo "IDEA" + ((org-agenda-overriding-header "\nIdeas\n------\n") + (org-agenda-todo-keyword-format "")))) + + ((org-agenda-compact-blocks t) + (org-agenda-remove-tags t))) + + ; List of done items. Useful for standups, review meetings, weekly + ; reports, etc. + ("d" "Done items" + ; First show the items done yesterday. Useful for standups. + ((todo "DONE" + ((org-agenda-overriding-header "Done yesterday\n---------------\n") + (org-agenda-skip-function + '(mssola-org-skip-if-not-closed-in-day + (time-subtract (current-time) (seconds-to-time 86400)))) + (org-agenda-todo-keyword-format ""))) + + ; Then show what I've done today. + (todo "DONE" + ((org-agenda-overriding-header "\nDone today\n-----------\n") + (org-agenda-skip-function + '(mssola-org-skip-if-not-closed-in-day + (current-time))) + (org-agenda-todo-keyword-format ""))) + + ; Finally show what I've been doing in the past 15 days. Useful for + ; review meetings and weekly reports. + (todo "DONE" + ((org-agenda-start-day "-15d") + (org-agenda-span 15) + (org-agenda-start-on-weekday nil) + (org-agenda-todo-keyword-format "") + (org-agenda-scheduled-leaders '("" "")) + (org-agenda-overriding-header "\nDone during the past 15 days\n-----------------------------\n")))) + + ((org-agenda-compact-blocks t) + (org-agenda-remove-tags t))))) +</pre> +</div> + +<p> +The prefix for the different kinds of types being used. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(setq org-agenda-prefix-format '((agenda . "%t%s") + (tags . "%c:%s") + (todo . "%c:%t%s"))) +</pre> +</div> + +<p> +Insert a <kbd></kbd> value in org mode. See this <a href="http://emacs.stackexchange.com/questions/2206/i-want-to-have-the-kbd-tags-for-my-blog-written-in-org-mode">StackExchange answer</a>. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun endless/insert-key (key) + "Ask for a key then insert its description. +Will work on both org-mode and any mode that accepts plain html." + (interactive "kType key sequence: ") + (let* ((is-org-mode (derived-mode-p 'org-mode)) + (tag (if is-org-mode + "@@html:<kbd>%s</kbd>@@" + "<kbd>%s</kbd>"))) + (if (null (equal key "\r")) + (insert + (format tag (help-key-description key nil))) + (insert (format tag "")) + (forward-char (if is-org-mode -8 -6))))) + +(define-key org-mode-map "\C-ck" #'endless/insert-key) +</pre> +</div> +</div> + +<div id="outline-container-sec-13-1" class="outline-3"> +<h3 id="sec-13-1"><span class="section-number-3">13.1</span> <span class="todo TODO">TODO</span> shortcut for making an org link, and transforming a link into a proper org link</h3> +</div> +<div id="outline-container-sec-13-2" class="outline-3"> +<h3 id="sec-13-2"><span class="section-number-3">13.2</span> <span class="todo TODO">TODO</span> make it work with evil</h3> +</div> +<div id="outline-container-sec-13-3" class="outline-3"> +<h3 id="sec-13-3"><span class="section-number-3">13.3</span> <span class="todo TODO">TODO</span> Proper keybindings for quick access.</h3> +</div> +</div> + +<div id="outline-container-sec-14" class="outline-2"> +<h2 id="sec-14"><span class="section-number-2">14</span> Languages</h2> +<div class="outline-text-2" id="text-14"> +<p> +First of all, define a function that identifies some warning keywords +(e.g. TODO). This function can then be applied to the proper mode. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun warnings-mode-hook () + "Hook for enabling the warning face on strings with a warning prefix." + + (font-lock-add-keywords nil + '(("\\(XXX\\|FIXME\\|TODO\\|HACK\\|NOTE\\)" + 1 font-lock-warning-face prepend)))) +</pre> +</div> + +<p> +Text mode is not a programming language, but it's used quite often in this +context too. In this case, I want spell check and <code>wc-mode</code> activated. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(add-hook 'text-mode-hook + (lambda () + (flyspell-mode 1) + (wc-mode 1))) +</pre> +</div> + +<p> +Emacs lisp needs <code>rainbow-delimiters</code>, so the amount of parenthesis is less +confusing. Moreover, I'm also enabling <code>eldoc-mode</code> and the aforementioned +<code>warnings-mode-hook</code>. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(add-hook 'emacs-lisp-mode-hook + (lambda () + (eldoc-mode 1) + (warnings-mode-hook) + (rainbow-delimiters-mode 1) + ; https://github.com/jhenahan/emacs.d/blob/master/emacs-init.org#emacs-lisp + (setq mode-name "ξ"))) +</pre> +</div> + +<p> +C and C++ only require the <code>warnings-mode-hook</code> function, spell checking for the +comments and the usage of tabs instead of spaces. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">; Note that C-common includes languages with a similar syntax of C. +(add-hook 'c-mode-common-hook 'warnings-mode-hook) +(add-hook 'c-mode-common-hook (lambda() (flyspell-prog-mode))) + +;; C +(add-hook 'c-mode-hook + (lambda () (setq indent-tabs-mode t))) + +;; C++ +(add-hook 'c++-mode-hook + (lambda () (setq indent-tabs-mode t))) +</pre> +</div> + +<p> +Include warning keywords in Ruby. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(add-hook 'ruby-mode-hook 'warnings-mode-hook) +</pre> +</div> + +<p> +And now my Go configuration. This includes stuff like the usage of <code>goimports</code>, +<code>gofmt</code> on save, among many other useful things. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(defun mssola-go-mode () + "My configuration for Go mode." + + ; Use goimports instead of go-fmt + (setq gofmt-command "goimports") + + ; Call Gofmt before saving + (add-hook 'before-save-hook 'gofmt-before-save) + + ; Integration flycheck with Go + (add-to-list 'load-path + (concat (getenv "GOPATH") "/src/github.com/dougm/goflymake")) + (require 'go-flycheck) + + (evil-leader/set-key + "." 'godef-jump-other-window) + + (setq indent-tabs-mode t) + (flyspell-prog-mode) + + ; eldoc support + (use-package go-eldoc + :ensure t + :config + (require 'go-eldoc)) + + (use-package go-add-tags + :ensure t + :config + (evil-leader/set-key "t" 'go-add-tags))) + +;; Go +(use-package go-mode + :ensure t + :pin melpa-stable + :config + + (add-hook 'go-mode-hook 'warnings-mode-hook) + (add-hook 'go-mode-hook 'go-eldoc-setup) + (add-hook 'go-mode-hook 'mssola-go-mode)) +</pre> +</div> + +<p> +Tabs or spaces? <a href="https://www.emacswiki.org/emacs/TabsSpacesBoth">Both</a>. The <code>smart-tabs-mode</code> has the philosophy of: tabs for +indentation, spaces for alignment. This is only applied in languages where I'm +usings tabs for indentation (C, C++ and Go). +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package smart-tabs-mode + :ensure t + :config + (smart-tabs-add-language-support golang go-mode-hook + ((c-indent-line . c-basic-offset) + (c-indent-region . c-basic-offset))) + (smart-tabs-insinuate 'c 'c++ 'golang)) +</pre> +</div> + +<p> +Inferior markup languages. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">;; Markdown mode with preview mode in the browser. +(use-package markdown-mode + :ensure t + :config + + ; This is the one that I got from openSUSE. + (custom-set-variables + '(markdown-command "/usr/bin/markdown-calibre")) + + ; Preview mode does its things through websockets, so it's a requirement. + ; After that, we can safely require it. + (use-package websocket + :ensure t + :config + (use-package markdown-preview-mode + :ensure t))) + +; YAML +(use-package yaml-mode + :ensure t + :config + + (add-hook 'yaml-mode-hook 'warnings-mode-hook)) +</pre> +</div> + +<p> +Web-related stuff. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package slim-mode + :ensure t) + +(use-package scss-mode + :ensure t + :config + + (setq scss-compile-at-save nil) + (add-hook 'yaml-mode-hook 'warnings-mode-hook)) + +(use-package php-mode + :ensure t) + +(use-package web-mode + :ensure t + :config + + (add-to-list 'auto-mode-alist '("\\.erb\\'" . web-mode)) + (add-to-list 'auto-mode-alist '("\\.jinja\\'" . web-mode)) + (add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode)) + + (add-hook 'web-mode-hook + (lambda () + (setq web-mode-markup-indent-offset 2) + (setq web-mode-css-indent-offset 2) + (setq web-mode-code-indent-offset 2)))) +</pre> +</div> + +<p> +CMake for y'all. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package cmake-mode + :ensure t + :config + + (setq auto-mode-alist + (append + '(("CMakeLists\\.txt\\'" . cmake-mode)) + '(("\\.cmake\\'" . cmake-mode)) + auto-mode-alist)) + + (use-package cmake-font-lock + :ensure t + :config + + (add-hook 'cmake-mode-hook 'cmake-font-lock-activate) + (add-hook 'yaml-mode-hook 'warnings-mode-hook))) +</pre> +</div> + +<p> +Some devops stuff, like Docker, terraform, etc. I have salt-mode disabled for +now because it's buggy, but I still have hope. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package dockerfile-mode + :ensure t + :config + + (add-to-list 'auto-mode-alist '("Dockerfile\\'" . dockerfile-mode))) + +(use-package terraform-mode + :ensure t + :config + + (terraform-format-on-save-mode)) + +(use-package hcl-mode + :ensure t) + +;(use-package salt-mode + ;:ensure t) +</pre> +</div> + +<p> +The <i>soria</i> theme has the <code>soria-purple-identifiers</code> hook. This hook instructs +the theme to use purple for identifiers instead of the default color. This is a +remnant from my Vim times, and I only apply it to some languages (random +criteria really). +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(dolist (lang-hook '(ruby-mode-hook + php-mode-hook + perl-mode-hook + emacs-lisp-mode-hook)) + (add-hook lang-hook 'soria-purple-identifiers)) +</pre> +</div> + +<p> +Last but not least, <code>YASnippet</code>. This package allows people to define shortcuts +for writing some common blocks. +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package yasnippet + :ensure t + :config + (yas-reload-all) + (add-hook 'prog-mode-hook #'yas-minor-mode)) +</pre> +</div> +</div> +</div> + +<div id="outline-container-sec-15" class="outline-2"> +<h2 id="sec-15"><span class="section-number-2">15</span> Misc</h2> +<div class="outline-text-2" id="text-15"> +<p> +Install a set of useful functions from <a href="https://github.com/bbatsov">@bbatsov</a>. The bindings are following +Emacs style instead of being more Vim-like on purpose (I don't want to put +too many things into my leader and these shortcuts look sensible to me). +</p> + +<div class="org-src-container"> + +<pre class="src src-elisp">(use-package crux + :ensure t + :bind (("C-c d" . crux-delete-file-and-buffer) + ("C-c r" . crux-rename-file-and-buffer) + ("C-c i" . crux-find-user-init-file) + ("C-c o" . crux-open-with))) +</pre> +</div> +</div> +</div> +</div> +<div id="postamble" class="status"> +<p class="author">Author: Miquel Sabaté Solà</p> +<p class="date">Created: 2017-01-14 Sat 15:56</p> +<p class="creator"><a href="http://www.gnu.org/software/emacs/">Emacs</a> 25.1.1 (<a href="http://orgmode.org">Org</a> mode 8.2.10)</p> +<p class="validation"><a href="http://validator.w3.org/check?uri=referer">Validate</a></p> +</div> +</body> +</html> diff --git a/emacs/init.org b/emacs/init.org index 9be404f..a68216e 100644 --- a/emacs/init.org +++ b/emacs/init.org @@ -1,7 +1,7 @@ #+TITLE: mssola's GNU Emacs configuration #+AUTHOR: Miquel Sabaté Solà #+BABEL: :cache yes -#+PROPERTY: header-args :tangle init.el +#+PROPERTY: header-args :tangle ~/.emacs.d/init.el * Preface <<sec:preface>> @@ -14,38 +14,49 @@ byte-compiled. This means that all changes are to be made on the =init.org= file *always*. The initial contents of the =init.el= are: #+BEGIN_SRC elisp :tangle no - ;; We can't tangle without org! - (require 'org) +;; We can't tangle without org! +(require 'org) - ;; Open the configuration - (find-file (concat user-emacs-directory "init.org")) +;; Follow symlinks +(setq vc-follow-symlinks t) - ;; Tangle it - (org-babel-tangle) +;; Open the configuration +(find-file (concat user-emacs-directory "init.org")) - ;; Load it - (load-file (concat user-emacs-directory "init.el")) +;; Tangle it +(org-babel-tangle) - ;; Finally byte-compile it - (byte-compile-file (concat user-emacs-directory "init.el")) +;; Load it +(load-file (concat user-emacs-directory "init.el")) + +;; Finally byte-compile it +(byte-compile-file (concat user-emacs-directory "init.el")) #+END_SRC There is no reason to track the init.el file, and no reason to change this -file. Moreover, in order to avoid manually tangling and compiling the -=init.org= file on each change, the following function is provided: +file. In order to ensure this, make sure to apply the following command after +cloning: + +#+BEGIN_SRC sh +git update-index --assume-unchanged emacs/init.el +#+END_SRC + +Moreover, in order to avoid manually tangling and compiling the =init.org= file +on each change, the following function is provided: #+BEGIN_SRC elisp (defun tangle-init () "If the current buffer is 'init.org' the code-blocks are - tangled, and the tangled file is compiled." + tangled, and the tangled file is compiled. Morever, an init.html is generated." (when (or (equal (buffer-file-name) (expand-file-name (concat user-emacs-directory "init.org"))) (string-suffix-p "dotfiles/emacs/init.org" (buffer-file-name))) - ;; Avoid running hooks when tangling. + ;; Avoid running hooks when tangling (let ((prog-mode-hook nil)) (org-babel-tangle) + (org-html-export-to-html) (byte-compile-file (concat user-emacs-directory "init.el"))))) (add-hook 'after-save-hook 'tangle-init) |
