From cc95fcb1344b1629f180e13edcc77b4a2628ecc9 Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Sat, 14 Jan 2017 15:56:29 +0100 Subject: emacs: added an html file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Miquel Sabaté Solà --- emacs/init.html | 2163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2163 insertions(+) create mode 100644 emacs/init.html (limited to 'emacs/init.html') 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 @@ + + + + +mssola's GNU Emacs configuration + + + + + + + + +
+

mssola's GNU Emacs configuration

+ + +
+

1 Preface

+
+

+ +

+ +

+This is my attempt of getting my configuration organized with literate +programming and org-babel. I took the workflow I'm following from @larstvei. +The idea is that the init.el 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 init.org +file always. The initial contents of the init.el are: +

+ +
+ +
;; 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"))
+
+
+ +

+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: +

+ +
+ +
git update-index --assume-unchanged emacs/init.el
+
+
+ +

+Moreover, in order to avoid manually tangling and compiling the init.org file +on each change, the following function is provided: +

+ +
+ +
(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)
+
+
+
+
+
+

2 Introduction

+
+

+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. +

+ +
+ +
(unless (>= emacs-major-version 24)
+  (error "Don't be a cheap bastard and upgrade to at least GNU Emacs 24"))
+
+
+ +

+Temporarily reduce garbage collection so startup time is lower. Idea taken from +@purcell. +

+ +
+ +
(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)))
+
+
+ +

+User name and email. +

+ +
+ +
(setq user-full-name "Miquel Sabaté Solà"
+      user-mail-address "mikisabate@gmail.com")
+
+
+
+
+ +
+

3 General

+
+
+

3.1 GUI

+
+

+I like a minimalistic GUI. Because of this, almost all GUI elements have been +disabled or tweaked in some custom way. +

+ +

+The frame title is "<login>: <path>". If we are not editing a file, then the +name of the buffer is displayed (e.g. "mssola: scratch"). +

+ +
+ +
(setq frame-title-format
+  '((:eval
+    (concat (user-real-login-name) ": "
+      (if (buffer-file-name)
+        (abbreviate-file-name (buffer-file-name))
+        "%b")))))
+
+
+ +

+Disable the menu, scroll and tool bars. At the same time, enable line and column +modes. +

+ +
+ +
(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)
+
+
+
+
+ +
+

3.2 Basic editing configuration

+
+

+Use UTF-8 always. +

+ +
+ +
(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)
+
+
+
+
+ +
+

3.3 Font and theme

+
+

+I'm using "Droid Sans Mono" simply because I've grown used to it. +

+ +
+ +
(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")))
+
+
+ +

+I've hacked my own theme called soria. This theme combines the vim theme +xoria256 with the openSUSE branding guidelines. +

+ +
+ +
(load-theme 'soria t)
+
+
+
+
+ +
+

3.4 General global key bindings

+
+

+Use kill-this-buffer instead of kill-buffer. +

+ +
+ +
(global-set-key (kbd "C-x k") 'kill-this-buffer)
+
+
+ +

+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. +

+ +
+ +
(global-unset-key (kbd "C-z"))
+
+
+ +

+Disable all the Fn keys. +

+ +
+ +
(dotimes (i 12)
+  (global-unset-key (kbd (format "<f%d>" (+ i 1)))))
+
+
+ +

+Disable overwrite-mode. +

+ +
+ +
(define-key global-map [(insert)] nil)
+
+
+
+
+ +
+

3.5 Others

+
+

+Revert buffers automatically when underlying files are changed externally. +

+ +
+ +
(global-auto-revert-mode t)
+
+
+ +

+Follow symlinks. +

+ +
+ +
(setq vc-follow-symlinks t)
+
+
+ +

+Remove the initial message from the scratch buffer. +

+ +
+ +
(setq initial-scratch-message nil)
+
+
+ +

+Never kill the scratch buffer, bury it instead. +

+ +
+ +
(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)))
+
+
+ +

+No backups +

+ +
+ +
(setq-default make-backup-files nil)
+(setq-default auto-save-default nil)
+
+
+ +

+No welcome screen +

+ +
+ +
(setq-default inhibit-startup-message t)
+
+
+ +

+Enable y/n answers +

+ +
+ +
(fset 'yes-or-no-p 'y-or-n-p)
+
+
+ +

+Let flyspell be performant. +

+ +
+ +
(defvar flyspell-issue-message-flag nil)
+
+
+ +

+Save custom-variables somewhere else. +

+ +
+ +
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
+(if (file-exists-p custom-file)
+    (load custom-file))
+
+
+
+
+
+ +
+

4 Calendar

+
+

+We catalans start our weeks on Monday. +

+ +
+ +
(defvar calendar-week-start-day 1)
+
+
+ +

+Global key binding. +

+ +
+ +
(global-set-key (kbd "C-c c") 'calendar)
+
+
+ +

+Fix some stuff for evil mode. +

+ +
+ +
(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))
+
+
+
+
+ +
+

5 General purpose defuns

+
+

+I want to read the latest news. That's why I define a function that downloads +the NEWS file from the git server and then opens it in a buffer. +

+ +
+ +
(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))
+
+
+ +

+Sometimes I want to debug my initialization time. +

+ +
+ +
(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)))
+
+
+
+
+ +
+

6 Lisp packages

+
+
+

6.1 Custom packages

+
+

+Compile the g.el script and bind it to M-g. +

+ +
+ +
(byte-compile-file (concat user-emacs-directory "lisp/g.el") t)
+(global-set-key (kbd "M-g") 'g)
+
+
+
+
+ +
+

6.2 use-package

+
+

+Initialize package. +

+ +
+ +
(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)
+
+
+ +

+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. +

+ +
+ +
(unless (package-installed-p 'use-package)
+  (package-refresh-contents)
+  (package-install 'use-package))
+
+
+
+
+
+ +
+

7 Project

+
+

+First of all, load the silver searcher, which is a convenient and fast searcher. +Ayo silver! +

+ +
+ +
(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))
+
+
+ +

+Then, for keeping up with my projects I use the Projectile + Helm combination. +

+ +
+ +
(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))
+
+
+ +

+Finally, I've added a couple of bindings in evil mode. First of all, I want +C-p to use the proper function for finding files. +

+ +
+ +
(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))
+
+
+ +

+Similarly, helm-ag has two functions for applying ag on the project. I'm +binding to ,a a function that calls to the proper function. +

+ +
+ +
(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))
+
+
+
+
+ +
+

8 Edit

+
+

+In this section I define some useful packages for editing. First of all, one of +the coolest packages out there is undo-tree. 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. +

+ +
+ +
(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)))
+
+
+ +

+Another important package is flycheck, which is a on-the-fly syntax checking +extension. This works with lots of languages with proper glue code. +

+ +
+ +
(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))
+
+
+ +

+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 +default-text-scale package for this. +

+ +
+ +
(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))
+
+
+ +

+Some languages use some delimiters a lot (e.g. lisp languages and +parenthesis). For this reason I'm using the rainbow-delimiters package, which +properly highlights each level in a different way (provided that your theme +supports it). +

+ +
+ +
(use-package rainbow-delimiters
+  :ensure t)
+
+
+ +

+I never use the mouse. +

+ +
+ +
(use-package disable-mouse
+  :ensure t
+  :config
+  (global-disable-mouse-mode))
+
+
+ +

+Sometimes you begin typing a prefix, but then you forget the following +chord. For this reason which-key was created. It will show the available +commands for the current chord as a list. +

+ +
+ +
(use-package which-key
+  :ensure t)
+
+
+ +

+For some modes it is important to count the number of words in the text. For +this, we have wc-mode. +

+ +
+ +
(use-package wc-mode
+  :ensure t)
+
+
+ +

+Next on the list, we have writeroom-mode. I honestly don't use this package +that often, but it's cool to have a distraction-free screen from time to time. +

+ +
+ +
(use-package writeroom-mode
+  :ensure t)
+
+
+ +

+Finally, we have expand-region. 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. +

+ +
+ +
(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)
+
+
+
+
+ +
+

9 Dired

+
+

+I use dired mode mainly for attaching document into emails. That being said, +whenever I use it, I want basic evil movement. +

+ +
+ +
(with-eval-after-load 'evil
+  (evil-add-hjkl-bindings dired-mode-map 'normal
+    (kbd "w") 'evil-forward-word-begin))
+
+
+ +

+And now instruct dired mode how to attach files when using mu4e. This is taken +from the mu4e documentation, and it's available by typing +C-cRETC-a. +

+ +
+ +
(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)
+
+
+
+
+ +
+

10 Evil

+
+

+Forgive me, Father, for I have sinned. As I said in the Preface, 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. +

+ +
+ +
(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)))
+
+
+
+
+ +
+

11 Magit

+
+

+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). +

+ +
+ +
(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))))
+
+
+
+
+ +
+

12 mu4e

+
+

+I use mu and mu4e 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 emacs/README.org file as +provided in my dotfiles project to get more details. +

+ +

+I'm using an RPM that I've built on OBS which installs mu4e globally. +

+ +
+ +
(add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e")
+(require 'mu4e)
+
+(when (featurep 'mu4e)
+
+
+ +

+Diferent SMTP options that will be used for each context. +

+ +
+ +
(setq message-send-mail-function 'smtpmail-send-it
+      mu4e-maildir (expand-file-name "~/.mail")
+      starttls-use-gnutls t)
+
+
+ +

+After that, I am defining some functions that will be used in various parts of +the configuration. +

+ +
+ +
(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))))
+
+
+ +

+Now it's time to define the different contexts that I have. Defining contexts +this way is relatively new (since mu 0.16). +

+ +
+ +
(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")))))
+
+
+ +

+If mu4e cannot figure things out, ask me. +

+ +
+ +
(setq mu4e-context-policy 'ask)
+(setq mu4e-compose-context-policy 'ask)
+
+
+ +

+Fill the mu4e-user-mail-address-list variable with the contexts. +

+ +
+ +
(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)))
+
+
+ +

+Setting my bookmarks +

+ +
+ +
(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)))
+
+
+ +

+The following signature looks alright regardless of whether the client supports +format=flowed or not. +

+ +
+ +
(setq mu4e-compose-signature
+      (concat
+       "Miquel Sabaté Solà,\n"
+       "PGP: 4096R / 1BA5 3C7A C93D CA2A CFDF DA97 96BE 8C6F D89D 6565\n"))
+
+
+ +

+Sign outgoing emails always. +

+ +
+ +
(add-hook 'message-send-hook 'mml-secure-message-sign-pgpmime)
+
+
+ +

+To avoid UID clashes. See this. +

+ +
+ +
(setq mu4e-change-filenames-when-moving t)
+
+
+ +

+Miscellaneous settings. +

+ +
+ +
(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)
+
+
+ +

+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: +

+ +
+ +
(setq mu4e-headers-fields
+      '( (:date          .  18)
+         (:mailing-list  .  15)
+         (:from-or-to    .  20)
+         (:subject       .  nil)))
+
+
+ +

+Show images +

+ +
+ +
(setq mu4e-view-show-images t
+      mu4e-view-image-max-width 800)
+
+; Use imagemagick, if available
+(when (fboundp 'imagemagick-register-types)
+  (imagemagick-register-types))
+
+
+ +

+As of 0.9.18 and GNU Emacs 25, the mu4e-action-with-xwidget can be used to +render an HTML message with Webkit. +

+ +
+ +
(if (>= emacs-major-version 25)
+    (add-to-list 'mu4e-view-actions
+                 '("webkit" . mu4e-action-view-with-xwidget)))
+
+
+ +

+Look for mu4e-msg2pdf in the exec path. The reason for this is that that OBS +package installs mu's toys into the exec path, but mu4e doesn't really count +on it. +

+ +
+ +
(let ((exec (locate-file "msg2pdf" exec-path exec-suffixes)))
+  (if exec
+      (setq mu4e-msg2pdf exec)))
+
+
+ +

+Adding hooks for composing and viewing messages. +

+ +
+ +
(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)))
+
+
+ +

+I want desktop notifications when receiving email. +

+ +
+ +
(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)))))
+
+
+ +

+And finally define a proper shortcut. +

+ +
+ +
; The trailing parenthesis closes the "(when (featurep 'mu4e)" statement from
+; the very beginning.
+(global-set-key (kbd "C-c m") 'mu4e))
+
+
+
+
+ +
+

13 org

+
+

+First of all, let me define some helper functions. +

+ +
+ +
(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)))
+
+
+ +

+Some general UI settings for org mode. +

+ +
+ +
(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)))
+
+
+ +

+Loggins settings. +

+ +
+ +
(setq org-log-done t)
+(setq org-log-redeadline (quote time))
+(setq org-log-reschedule (quote time))
+
+
+ +

+Where org files reside. +

+ +
+ +
(setq org-agenda-files '("~/org/"))
+
+
+ +

+Custom commands for org-agenda. +

+ +
+ +
(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)))))
+
+
+ +

+The prefix for the different kinds of types being used. +

+ +
+ +
(setq org-agenda-prefix-format '((agenda . "%t%s")
+                                 (tags   . "%c:%s")
+                                 (todo   . "%c:%t%s")))
+
+
+ +

+Insert a <kbd></kbd> value in org mode. See this StackExchange answer. +

+ +
+ +
(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)
+
+
+
+ +
+

13.1 TODO shortcut for making an org link, and transforming a link into a proper org link

+
+
+

13.2 TODO make it work with evil

+
+
+

13.3 TODO Proper keybindings for quick access.

+
+
+ +
+

14 Languages

+
+

+First of all, define a function that identifies some warning keywords +(e.g. TODO). This function can then be applied to the proper mode. +

+ +
+ +
(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))))
+
+
+ +

+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 wc-mode activated. +

+ +
+ +
(add-hook 'text-mode-hook
+          (lambda ()
+            (flyspell-mode 1)
+            (wc-mode 1)))
+
+
+ +

+Emacs lisp needs rainbow-delimiters, so the amount of parenthesis is less +confusing. Moreover, I'm also enabling eldoc-mode and the aforementioned +warnings-mode-hook. +

+ +
+ +
(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 "ξ")))
+
+
+ +

+C and C++ only require the warnings-mode-hook function, spell checking for the +comments and the usage of tabs instead of spaces. +

+ +
+ +
; 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)))
+
+
+ +

+Include warning keywords in Ruby. +

+ +
+ +
(add-hook 'ruby-mode-hook 'warnings-mode-hook)
+
+
+ +

+And now my Go configuration. This includes stuff like the usage of goimports, +gofmt on save, among many other useful things. +

+ +
+ +
(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))
+
+
+ +

+Tabs or spaces? Both. The smart-tabs-mode 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). +

+ +
+ +
(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))
+
+
+ +

+Inferior markup languages. +

+ +
+ +
;; 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))
+
+
+ +

+Web-related stuff. +

+ +
+ +
(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))))
+
+
+ +

+CMake for y'all. +

+ +
+ +
(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)))
+
+
+ +

+Some devops stuff, like Docker, terraform, etc. I have salt-mode disabled for +now because it's buggy, but I still have hope. +

+ +
+ +
(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)
+
+
+ +

+The soria theme has the soria-purple-identifiers 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). +

+ +
+ +
(dolist (lang-hook '(ruby-mode-hook
+                     php-mode-hook
+                     perl-mode-hook
+                     emacs-lisp-mode-hook))
+  (add-hook lang-hook 'soria-purple-identifiers))
+
+
+ +

+Last but not least, YASnippet. This package allows people to define shortcuts +for writing some common blocks. +

+ +
+ +
(use-package yasnippet
+  :ensure t
+  :config
+  (yas-reload-all)
+  (add-hook 'prog-mode-hook #'yas-minor-mode))
+
+
+
+
+ +
+

15 Misc

+
+

+Install a set of useful functions from @bbatsov. 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). +

+ +
+ +
(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)))
+
+
+
+
+
+
+

Author: Miquel Sabaté Solà

+

Created: 2017-01-14 Sat 15:56

+

Emacs 25.1.1 (Org mode 8.2.10)

+

Validate

+
+ + -- cgit v1.2.3