diff options
| author | Miquel Sabaté Solà <msabate@suse.com> | 2019-08-22 18:30:24 +0200 |
|---|---|---|
| committer | Miquel Sabaté Solà <msabate@suse.com> | 2019-08-22 18:30:24 +0200 |
| commit | 4ead9be4468d8d58c027bf6e6616e3b4d6e727eb (patch) | |
| tree | 18ab19fd5213c9d302b165eafa46a6f1f8902e75 /.emacs.d/init.org | |
| parent | 04f4a7df197ce327f36b666869a1ca2f09934e58 (diff) | |
| download | dotfiles-4ead9be4468d8d58c027bf6e6616e3b4d6e727eb.tar.gz dotfiles-4ead9be4468d8d58c027bf6e6616e3b4d6e727eb.zip | |
Complete re-structuring so it's easier to install
Signed-off-by: Miquel Sabaté Solà <msabate@suse.com>
Diffstat (limited to '.emacs.d/init.org')
| -rw-r--r-- | .emacs.d/init.org | 2349 |
1 files changed, 2349 insertions, 0 deletions
diff --git a/.emacs.d/init.org b/.emacs.d/init.org new file mode 100644 index 0000000..66ed281 --- /dev/null +++ b/.emacs.d/init.org @@ -0,0 +1,2349 @@ +#+TITLE: mssola's GNU Emacs configuration +#+AUTHOR: Miquel Sabaté Solà +#+EMAIL: mikisabate@gmail.com +#+BABEL: :cache yes +#+PROPERTY: header-args :tangle ~/.emacs.d/init.el + +* Preface + +This is my attempt of getting my configuration organized with literate +programming and =org-babel=. I took the workflow I'm following from [[https://github.com/larstvei][@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: + +#+BEGIN_SRC elisp :tangle no +;; 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")) +#+END_SRC + +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: + +#+BEGIN_SRC sh :tangle no +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. 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) + (byte-compile-file (concat user-emacs-directory "init.el"))))) + +(add-hook 'after-save-hook 'tangle-init) +#+END_SRC + +* Introduction + +I'm using GNU Emacs 26, 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. + +#+BEGIN_SRC elisp + (unless (>= emacs-major-version 25) + (error "Don't be a cheap bastard and upgrade to at least GNU Emacs 25")) +#+END_SRC + +Temporarily reduce garbage collection so startup time is lower. Idea taken from +[[https://github.com/purcell][@purcell]]. + +#+BEGIN_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))) +#+END_SRC + +User name and email. + +#+BEGIN_SRC elisp +(setq user-full-name "Miquel Sabaté Solà" + user-mail-address "mikisabate@gmail.com") +#+END_SRC + +* Lisp packages +** Custom packages + +Compile the =g.el= script and bind it to @@html:<kbd>M-g</kbd>@@. + +#+BEGIN_SRC elisp +(if (file-exists-p (concat user-emacs-directory "lisp/g.elc")) + (load-file (concat user-emacs-directory "lisp/g.elc")) + (byte-compile-file (concat user-emacs-directory "lisp/g.el") t)) +(global-set-key (kbd "M-g") 'g) +#+END_SRC + +** use-package + +Initialize package. + +#+BEGIN_SRC elisp +(require 'package) + +(setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3") + +(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) +#+END_SRC + +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. + +#+BEGIN_SRC elisp +(unless (package-installed-p 'use-package) + (package-refresh-contents) + (package-install 'use-package)) + +(require 'use-package) +#+END_SRC + +Some =use-package= calls require =diminish.el= to be available. So, let's +require it here on the very top. + +#+BEGIN_SRC elisp +(use-package diminish + :ensure t) +#+END_SRC + +* General + +** 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*"). + +#+BEGIN_SRC elisp + (setq frame-title-format + '((:eval + (concat (user-real-login-name) ": " + (if (buffer-file-name) + (abbreviate-file-name (buffer-file-name)) + "%b"))))) +#+END_SRC + +Disable the menu, scroll and tool bars. At the same time, enable line and column +modes. + +#+BEGIN_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) +#+END_SRC + +Relative line numbers support is builtin since GNU Emacs 26.1: + +#+BEGIN_SRC elisp +(if (>= emacs-major-version 26) + (progn + (add-hook 'prog-mode-hook 'display-line-numbers-mode) + (setq display-line-numbers-type 'relative) + (global-set-key (kbd "C-c L") 'display-line-numbers-mode))) +#+END_SRC + +** Basic editing configuration + +Use UTF-8 *always*. + +#+BEGIN_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) +#+END_SRC + +Some editing tweaks like tabs vs spaces, maximum column width, etc. + +#+BEGIN_SRC elisp + ;; Emacs modes typically provide a standard means to change the indentation + ;; width (e.g. c-basic-offset). Moreover, even though I prefer tabs over space, + ;; for most coding conventions this is not the case (e.g. ruby). For this + ;; reason, I will disable them by default and enabled them back for each + ;; specific case (e.g. C). I'm also using the smart-tabs-mode package, see + ;; below in the languages section. + (setq-default indent-tabs-mode nil) + (setq-default tab-width 4) + + ;; Maximum 80 columns. + (setq-default fill-column 80) + (setq-default auto-fill-function 'do-auto-fill) + + ;; Do not break lines + (set-default 'truncate-lines t) + + ;; Delete the selection with a keypress. + (delete-selection-mode t) + + ;; Remove whitespaces at the end of line + (add-hook 'before-save-hook 'delete-trailing-whitespace) + + ;; Cursor + (blink-cursor-mode 0) + (global-hl-line-mode -1) + (show-paren-mode 1) +#+END_SRC + +** Font and theme + +I'm using "Droid Sans Mono" simply because I've grown used to it. + +#+BEGIN_SRC elisp +(defconst mssola-font + (if (string= system-type "darwin") + "Droid Sans Mono-12" + "Droid Sans Mono Dotted for Powerline-10") + "The font to be used.") + +(set-frame-font mssola-font) +(add-to-list 'default-frame-alist `(font . ,mssola-font)) + +; 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 mssola-font))) +#+END_SRC + +I've hacked my own theme called [[https://github.com/mssola/soria][soria]]. This theme combines the vim theme +[[http://www.vim.org/scripts/script.php?script_id=2140][xoria256]] with the [[http://opensuse.github.io/branding-guidelines/][openSUSE branding guidelines]]. + +#+BEGIN_SRC elisp + (load-theme 'soria t) +#+END_SRC + +When hacking your own theme, sometimes you want to know what face is the one +that you see on the screen right now. This function from [[https://github.com/thblt/DotFiles][@thblt]] allows me to +get exactly that: + +#+BEGIN_SRC elisp +(defun mssola-face-at-point (pos) + "Writes a message with the name of the face at the current point. The POS + argument contains the current position of the cursor." + + (interactive "d") + + (let ((face (or (get-char-property (point) 'read-face-name) + (get-char-property (point) 'face)))) + (if face (message "Face: %s" face) (message "No face at %d" pos)))) + +(global-set-key (kbd "C-c f") 'mssola-face-at-point) +#+END_SRC + +** General global key bindings + +Use kill-this-buffer instead of kill-buffer. + +#+BEGIN_SRC elisp + (global-set-key (kbd "C-x k") 'kill-this-buffer) +#+END_SRC + +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. + +#+BEGIN_SRC elisp + (global-unset-key (kbd "C-z")) +#+END_SRC + +Also disable the =C-x i= binding, since I've never used the default behavior, +and it will be used as a prefix for inferior modes (e.g. /ielm/). + +#+BEGIN_SRC elisp + (global-unset-key (kbd "C-x i")) +#+END_SRC + +Disable all the Fn keys. + +#+BEGIN_SRC elisp + (dotimes (i 12) + (global-unset-key (kbd (format "<f%d>" (+ i 1))))) +#+END_SRC + +Disable overwrite-mode. + +#+BEGIN_SRC elisp + (define-key global-map [(insert)] nil) +#+END_SRC + +Kill GNU Emacs by hitting =C-x r q= (mnemonic /Really quit/). + +#+BEGIN_SRC elisp + (global-set-key (kbd "C-x r q") 'kill-emacs) +#+END_SRC + +** Others + +Revert buffers automatically when underlying files are changed externally. + +#+BEGIN_SRC elisp + (global-auto-revert-mode t) +#+END_SRC + +Follow symlinks. + +#+BEGIN_SRC elisp + (setq vc-follow-symlinks t) +#+END_SRC + +Remove the initial message from the scratch buffer. + +#+BEGIN_SRC elisp + (setq initial-scratch-message nil) +#+END_SRC + +Never kill the scratch buffer, bury it instead. + +#+BEGIN_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))) + +(defadvice kill-this-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))) +#+END_SRC + +No backups + +#+BEGIN_SRC elisp + (setq-default make-backup-files nil) + (setq-default auto-save-default nil) +#+END_SRC + +But at least save the list of recently open files. + +#+BEGIN_SRC elisp +(require 'recentf) + +(recentf-mode 1) +(global-set-key "\C-x\ \C-r" 'recentf-open-files) + +; Save the list every 5 minutes +(run-at-time nil (* 5 60) 'recentf-save-list) +#+END_SRC + +No welcome screen + +#+BEGIN_SRC elisp + (setq-default inhibit-startup-message t) +#+END_SRC + +Enable y/n answers + +#+BEGIN_SRC elisp + (fset 'yes-or-no-p 'y-or-n-p) +#+END_SRC + +Let flyspell be performant. + +#+BEGIN_SRC elisp + (defvar flyspell-issue-message-flag nil) +#+END_SRC + +Save custom-variables somewhere else. + +#+BEGIN_SRC elisp + (setq custom-file (expand-file-name "custom.el" user-emacs-directory)) + (if (file-exists-p custom-file) + (load custom-file)) +#+END_SRC + +Disable audio notifications. + +#+BEGIN_SRC elisp +(setq ring-bell-function 'ignore) +#+END_SRC + +* Calendar + +We catalans start our weeks on Monday. + +#+BEGIN_SRC elisp + (defvar calendar-week-start-day 1) +#+END_SRC + +Global key binding. + +#+BEGIN_SRC elisp + (global-set-key (kbd "M-c") 'calendar) +#+END_SRC + +Fix some stuff for evil mode. + +#+BEGIN_SRC elisp +(with-eval-after-load "calendar" + (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))) +#+END_SRC + +* 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. + +#+BEGIN_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)) +#+END_SRC + +Sometimes I want to debug my initialization time. + +#+BEGIN_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))) +#+END_SRC + +* Project + +First of all, load the silver searcher, which is a convenient and fast searcher. +Ayo silver! + +#+BEGIN_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)) +#+END_SRC + +Then, for keeping up with my projects I use the Projectile + Helm combination. + +#+BEGIN_SRC elisp + (use-package projectile + :ensure t + :config + (projectile-mode 1)) + + (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-inside-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) + + (global-set-key (kbd "M-x") 'helm-M-x) + + (use-package helm-ag + :ensure t)) + + (use-package helm-projectile + :ensure t + :config + (helm-projectile-on) + + ; Define M-p as a way to quickly list all the available projects. + (with-eval-after-load 'evil + (define-key evil-normal-state-map (kbd "M-p") + 'helm-projectile-switch-project))) +#+END_SRC + +I use @@html:<kbd>C-p</kbd>@@ as the binding for listing relevant files. This +binding works either by using =helm-projectile= or the regular =helm-find= +function. As a final touch, this binding also works for listing channels in ERC +buffers. + +#+BEGIN_SRC elisp +(defun mssola-erc-helm-buffer-list () + "Returns a list with the ERC buffers." + (mapcar 'buffer-name (erc-buffer-list))) + +(defconst mssola-helm-source-erc-channel-list + '((name . "ERC Channels") + (candidates . mssola-erc-helm-buffer-list) + (action . switch-to-buffer))) + +(defun mssola-erc-helm-switch-buffer () + "Use helm to select an active ERC buffer." + + (interactive) + + (helm :sources '(mssola-helm-source-erc-channel-list) + :buffer "*helm-erc-channels*")) + +(defun mssola-find-file () + "Call the proper Helm function for finding files." + + (interactive) + + (if (string= major-mode "erc-mode") + (mssola-erc-helm-switch-buffer) + (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)) +#+END_SRC + +Similarly, =helm-ag= has two functions for applying =ag= on the project. I'm +binding to @@html:<kbd>,a</kbd>@@ a function that calls to the proper function. + +#+BEGIN_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)) +#+END_SRC + +* 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. + +#+BEGIN_SRC elisp +(with-eval-after-load 'undo-tree + (global-undo-tree-mode 1) + + (setq undo-tree-visualizer-diff t + undo-tree-visualizer-timestamps t + undo-tree-visualizer-relative-timestamps t) + + (require 'diminish) + (diminish 'undo-tree-mode)) +#+END_SRC + +Another important package is =flycheck=, which is an on-the-fly syntax checking +extension. This works with lots of languages with proper glue code. + +#+BEGIN_SRC elisp +(use-package let-alist + :ensure t) + +(use-package flycheck + :ensure t + :diminish + :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)) +#+END_SRC + +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. + +#+BEGIN_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)) +#+END_SRC + +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). + +#+BEGIN_SRC elisp + (use-package rainbow-delimiters + :ensure t) +#+END_SRC + +Enable =electric-pair-mode=, which automatically closes pairs like brackets: + +#+BEGIN_SRC elisp +(electric-pair-mode 1) +#+END_SRC + +I never use the mouse. + +#+BEGIN_SRC elisp +(use-package disable-mouse + :ensure t + :config + (global-disable-mouse-mode) + (setq global-disable-mouse-mode-lighter "")) +#+END_SRC + +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. + +#+BEGIN_SRC elisp +(use-package which-key + :ensure t + :diminish which-key-mode + :config + (which-key-mode)) +#+END_SRC + +For some modes it is important to count the number of words in the text. For +this, we have =wc-mode=. + +#+BEGIN_SRC elisp + (use-package wc-mode + :ensure t) +#+END_SRC + +Editing files as root is a bit of a pain because usually the root user doesn't +have the same configuration as the current one, and attempting to do so can be +messy. So, instead of that, we could advice the =find-file= function so if the +file is not writable by the current user, then GNU Emacs will ask for editing +this same file as root: + +#+BEGIN_SRC elisp +(defadvice find-file (after find-file-sudo activate) + "Find file as root if necessary." + (if (and buffer-file-name + (not (file-writable-p buffer-file-name))) + (if (yes-or-no-p "Do you want to edit this file as root?") + (find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))) +#+END_SRC + +=YASnippet= allows people to define shortcuts for writing some common blocks. +Moreover, it comes with a set of builtin snippets already. Since I don't +remember some of these snippets, I've mapped @@html:<kbd>, h</kbd>@@ to +=yas-describe-tables=, which shows the available snippets in another buffer. + +#+BEGIN_SRC elisp +(use-package yasnippet + :ensure t + :diminish yas-minor-mode + :init (yas-global-mode) + :config + (yas-global-mode 1)) +#+END_SRC + +=bool-flip= is a very simple utility that toggles truthy/falsey values. + +#+BEGIN_SRC elisp +(use-package bool-flip + :ensure t + :config + (global-set-key (kbd "C-c b") 'bool-flip-do-flip)) +#+END_SRC + +=move-text= is a small package that allows you to move lines with a +keybinding. This might be feasible with Evil mode, but still this might help +when you want to move lines and keep the default registry empty: + +#+BEGIN_SRC elisp +(use-package move-text + :ensure t + :bind + (("M-k" . move-text-up) + ("M-j" . move-text-down))) +#+END_SRC + +* Dired + +I use dired mode mainly for attaching document into emails. That being said, +whenever I use it, I want basic evil movement. + +#+BEGIN_SRC elisp + (with-eval-after-load 'evil + (evil-add-hjkl-bindings dired-mode-map 'normal + (kbd "w") 'evil-forward-word-begin)) +#+END_SRC + +I also extend =dired= with some handy tweaks. + +#+BEGIN_SRC elisp +(setq directory-free-space-args "-Pkh" + dired-dwim-target t + dired-omit-mode nil + dired-recursive-copies 'always + dired-recursive-deletes 'always + delete-old-versions t) +#+END_SRC + +And now instruct dired mode how to attach files when using mu4e. This is taken +from the [[https://www.djcbsoftware.nl/code/mu/mu4e/Dired.html#Dired][mu4e documentation]], and it's available by typing +@@html:<kbd>C-c RET C-a</kbd>@@. + +#+BEGIN_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) +#+END_SRC + +* Evil + +Forgive me, [[https://stallman.org/saint.html][Father]], for I have sinned. 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 blocks include some heavy-lifting so +Evil and GNU Emacs work without hitting each other, and it also includes some +Evil extensions. + +First of all, let's define a function that will be called whenever Evil is loaded. + +#+BEGIN_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) + + ; Go back to Emacs' bindings on beginning/end of line. + (eval-after-load "evil-maps" + (dolist (map '(evil-motion-state-map + evil-insert-state-map + evil-emacs-state-map)) + (define-key (eval map) "\C-a" 'crux-move-beginning-of-line) + (define-key (eval map) "\C-e" 'end-of-line))) + + ; 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)))) +#+END_SRC + +Now make sure that Evil is installed, and call the relevant configuration functions. + +#+BEGIN_SRC elisp +(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) + + ;; 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) +#+END_SRC + +If Evil was properly loaded, then make sure that the following Evil-related +packages are installed and configured as well. I start by defining the +=evil-leader= package, which brings the @@html:<kbd>leader</kbd>@@ feature from +Vim into Evil. + +#+BEGIN_SRC elisp + (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 + "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)) +#+END_SRC + +Another handy Vim plugin that has made it into Evil is =evil-surround=, which +defines a new text object for surrounding characters (e.g. change a string from +having double quotes with single quotes in a single command). + +#+BEGIN_SRC elisp + (use-package evil-surround + :ensure t + :config + (global-evil-surround-mode 1)) +#+END_SRC + +Next is another Vim plugin that has been ported to Evil: =evil-commentary=. This +package defines a new motion for comments, which is bound to +@@html:<kbd>gc</kbd>@@. So, for example, @@html:<kbd>gcc</kbd>@@ will comment +the current line, regardless of the programming language. + +#+BEGIN_SRC elisp + (use-package evil-commentary + :ensure t + :config + (evil-commentary-mode t)) +#+END_SRC + +Another cool package is =evil-args= which defines the argument text object. This +text object can be targeted with the =a= character, and we can move backward and +forward through arguments with @@html:<kbd>H</kbd>@@ and @@html:<kbd>L</kbd>@@ +respectively. + +#+BEGIN_SRC elisp + (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)) +#+END_SRC + +Last but not least, =evil-numbers= brings a couple of bindings available to Vim +into Evil: @@html:<kbd>C-c +</kbd>@@ for increasing a number, and +@@html:<kbd>C-c -</kbd>@@ for decreasing it. + +#+BEGIN_SRC elisp + (use-package evil-numbers + :ensure t + :config + (define-key evil-normal-state-map (kbd "C-c +") 'evil-numbers/inc-at-pt) + (define-key evil-normal-state-map (kbd "C-c -") 'evil-numbers/dec-at-pt))) +#+END_SRC + +* Git + +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). + +#+BEGIN_SRC elisp +(use-package magit + :ensure t + :config +#+END_SRC + +Set some global key bindings for Magit. + +#+BEGIN_SRC elisp +(global-set-key (kbd "C-x g") 'magit-status) +(global-set-key (kbd "C-c l") 'magit-log-buffer-file) +#+END_SRC + +And repair some key bindings from Evil mode. + +#+BEGIN_SRC elisp + (with-eval-after-load 'evil + (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)))) +#+END_SRC + +=git-timemachine= is a package that goes hand-in-hand with Magit, and it +provides a very easy way to go through the history of a file (while providing +ways of jumping into Magit): + +#+BEGIN_SRC elisp +(use-package git-timemachine + :ensure t + :bind (("C-x t m" . git-timemachine-toggle))) +#+END_SRC + +* Email + +I use [[http://www.djcbsoftware.nl/code/mu/][mu]] and [[http://www.djcbsoftware.nl/code/mu/mu4e.html][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 [[https://github.com/mssola/dotfiles][dotfiles]] project to get more details. + +I'm using an RPM that I've built on [[https://build.opensuse.org/package/show/home:mssola/mu][OBS]] which installs mu4e globally. + +#+BEGIN_SRC elisp +(unless (file-directory-p "/usr/share/emacs/site-lisp/mu4e") + (message "Skipping mu4e because it's not installed.")) + +(when (file-directory-p "/usr/share/emacs/site-lisp/mu4e") + (add-to-list 'load-path "/usr/share/emacs/site-lisp/mu4e") + (require 'mu4e) + + (when (featurep 'mu4e) +#+END_SRC + +Set =mu4e= as the default user agent. This will be picked up by =compose-mail=. + +#+BEGIN_SRC elisp +(setq mail-user-agent 'mu4e-user-agent) +#+END_SRC + +Diferent SMTP options that will be used for each context. + +#+BEGIN_SRC elisp + (setq message-send-mail-function 'smtpmail-send-it + mu4e-maildir (expand-file-name "~/.mail") + starttls-use-gnutls t) +#+END_SRC + +After that, I am defining some functions that will be used in various parts of +the configuration. + +#+BEGIN_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 + message-send-mail-function 'smtpmail-send-it + smtpmail-smtp-service port)) + +; https://www.reddit.com/r/emacs/comments/47t9ec/share_your_mu4econtext_configs/d0fsih6 +(defun mu4e-message-maildir-matches (msg rx) + "Returns true if the maildir of MSG matches the given regexp 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" + + (if (string= key "susecom") + (setq archives-dir "/Arxiu/") + (setq archives-dir "/Archives/")) + (concat "/" key archives-dir + (format-time-string "%Y" (current-time)))) +#+END_SRC + +Depending on the context, it's better a signature or another: + +#+BEGIN_SRC elisp +(defun mssola-mu4e-signature (key) + "Returns a string containing the mail signature for the given KEY." + + (if (string= key "ajuntament") + (concat + "Miquel Sabaté Solà,\n" + "Regidor de Joventut, Participació ciutadana i Transparència\n" + "\n" + "Ajuntament de Capellades\n" + "Carrer de Ramon Godó, 9, 08786 - Capellades\n" + "Tel. 93 801 10 01 – mòbil 677 12 72 07\n" + "sabatesm@capellades.cat\n") + (concat + "Miquel Sabaté Solà,\n" + "PGP: 4096R / 1BA5 3C7A C93D CA2A CFDF DA97 96BE 8C6F D89D 6565\n"))) +#+END_SRC + +Now it's time to define the different contexts that I have. Defining contexts +this way is relatively new (since mu 0.9.16). + +#+BEGIN_SRC elisp +(setq mu4e-contexts + `( + ;; GMail + ,(make-mu4e-context + :name "gmail" + :enter-func (lambda () + (mu4e-message "Switching to gmail.com") + (setq mu4e-compose-signature (mssola-mu4e-signature "gmail")) + (setq mu4e-sent-messages-behavior 'delete) + (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"))) + + ;; City hall + ,(make-mu4e-context + :name "ajuntament" + :enter-func (lambda () + (mu4e-message "Switching to mail.diba.cat") + (setq mu4e-compose-signature (mssola-mu4e-signature "ajuntament")) + (setq mu4e-sent-messages-behavior 'sent) + (mssola-smtp "mail.diba.cat" 587) + (setq smtpmail-local-domain "capellades.cat")) + :match-func (lambda (msg) + (when msg + (mu4e-message-maildir-matches msg "^/ajuntament"))) + :vars '( + (user-mail-address . "sabatesm@capellades.cat") + (mu4e-reply-to-address . "sabatesm@capellades.cat") + (mu4e-drafts-folder . "/ajuntament/Esborranys") + (mu4e-sent-folder . "/ajuntament/Elements enviats") + (mu4e-refile-folder . "/ajuntament/Arxiu") + (mu4e-trash-folder . "/ajuntament/Elements suprimits"))) + + ;; suse.com + ,(make-mu4e-context + :name "comsuse" + :enter-func (lambda () + (mu4e-message "Switching to suse.com") + (setq mu4e-compose-signature (mssola-mu4e-signature "comsuse")) + (setq mu4e-sent-messages-behavior 'sent) + (mssola-smtp "smtp.office365.com" 587)) + :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/Esborranys") + (mu4e-sent-folder . "/susecom/Elements enviats") + (mu4e-refile-folder . ,(suse-refile-folder "susecom")) + (mu4e-trash-folder . "/susecom/Elements suprimits"))) + + ;; suse.de + ,(make-mu4e-context + :name "desuse" + :enter-func (lambda () + (mu4e-message "Switching to suse.de") + (setq mu4e-compose-signature (mssola-mu4e-signature "desuse")) + (setq mu4e-sent-messages-behavior 'sent) + (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"))))) +#+END_SRC + +If mu4e cannot figure things out, ask me. + +#+BEGIN_SRC elisp + (setq mu4e-context-policy 'ask) + (setq mu4e-compose-context-policy 'ask) +#+END_SRC + +Fill the =mu4e-user-mail-address-list= variable with the contexts. + +#+BEGIN_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))) +#+END_SRC + +Setting my bookmarks + +#+BEGIN_SRC elisp +(setq mu4e-bookmarks + '(("maildir:/gmail/inbox OR maildir:/susecom/inbox OR maildir:/susede/inbox OR maildir:/ajuntament/inbox" "Inbox Folders" ?n) + ("maildir:/gmail/Sent OR maildir:/susecom/Elements\\ enviats OR maildir:/susede/Sent OR maildir:/ajuntament/Elements\\ enviats" "Sent Folders" ?s) + ("flag:unread AND NOT flag:trashed" "Unread messages" ?u) + ("date:today..now" "Today's messages" ?t))) +#+END_SRC + +Sign outgoing emails always. + +#+BEGIN_SRC elisp + (add-hook 'message-send-hook 'mml-secure-message-sign-pgpmime) +#+END_SRC + +To avoid UID clashes. See [[http://pragmaticemacs.com/emacs/fixing-duplicate-uid-errors-when-using-mbsync-and-mu4e/][this]]. + +#+BEGIN_SRC elisp + (setq mu4e-change-filenames-when-moving t) +#+END_SRC + +Miscellaneous settings. + +#+BEGIN_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-view-show-addresses t + mu4e-headers-skip-duplicates t + mu4e-headers-include-related t + mu4e-headers-auto-update t) +#+END_SRC + +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: + +#+BEGIN_SRC elisp + (setq mu4e-headers-fields + '( (:date . 18) + (:mailing-list . 15) + (:from-or-to . 20) + (:subject . nil))) +#+END_SRC + +Add as a header action to toggle gnus mode for the view mode. I'm doing this +because this is way better to visualize attached .eml emails. + +#+BEGIN_SRC elisp +(defun mssola-toggle-gnus-mode (msg) + "Toggle gnus on view mode from now on." + (if mu4e-view-use-gnus + (setq mu4e-view-use-gnus nil) + (setq mu4e-view-use-gnus t))) + +(add-to-list 'mu4e-headers-actions + '("gnus mode toggle" . mssola-toggle-gnus-mode) t) +#+END_SRC + +Show images + +#+BEGIN_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)) +#+END_SRC + +Correct some key bindings that are screwed up by =evil-mode=: + +#+BEGIN_SRC elisp +(evil-define-key 'normal mu4e-view-mode-map + ";" 'mu4e-context-switch + "e" 'mu4e-view-save-attachment + "F" 'mu4e-compose-forward) +#+END_SRC + +As of 0.9.18 and GNU Emacs 25, the =mu4e-action-with-xwidget= can be used to +render an HTML message with Webkit. + +#+BEGIN_SRC elisp + (if (>= emacs-major-version 25) + (add-to-list 'mu4e-view-actions + '("webkit" . mu4e-action-view-with-xwidget))) +#+END_SRC + +Look for =mu4e-msg2pdf= in the exec path. The reason for this is that the OBS +package installs mu's =toys= into the exec path, but =mu4e= doesn't really count +on it. + +#+BEGIN_SRC elisp + (let ((exec (locate-file "msg2pdf" exec-path exec-suffixes))) + (if exec + (setq mu4e-msg2pdf exec))) +#+END_SRC + +Adding hooks for composing and viewing messages. + +#+BEGIN_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))) +#+END_SRC + +I want desktop notifications when receiving email. + +#+BEGIN_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 OR maildir:/ajuntament/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))))) +#+END_SRC + +And finally define a proper shortcut. + +#+BEGIN_SRC elisp + ; The trailing parenthesis closes the "(when (featurep 'mu4e)" statement from + ; the very beginning. + (global-set-key (kbd "C-c m") 'mu4e))) +#+END_SRC + +* org + +** TODO org template for creating "article" and similar stuff + +[[http://orgmode.org/][org mode]] is an incredible tool that keeps me organized: TODOs, notes, agenda, +etc. Moreover, it's built in GNU Emacs: + +#+BEGIN_SRC elisp +(require 'org) +#+END_SRC + +** General settings + +First of all, let me define some helper functions. + +#+BEGIN_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))) +#+END_SRC + +Some general UI settings for org mode. + +#+BEGIN_SRC elisp +(setq 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))) +#+END_SRC + +Logging settings. + +#+BEGIN_SRC elisp + (setq org-log-done t) + (setq org-log-redeadline (quote time)) + (setq org-log-reschedule (quote time)) +#+END_SRC + +Where org files reside. + +#+BEGIN_SRC elisp + (setq org-agenda-files '("~/org/")) +#+END_SRC + +** Publishing + +In order to publish files into HTML, I would like to have =htmlize= installed. +This package allows org to export to HTML in a better way (e.g. allowing code +blocks to be converted into HTML as well, so we can properly colorize it). + +#+BEGIN_SRC elisp +(use-package htmlize + :ensure t) +#+END_SRC + +And now let's set all the related settings. + +#+BEGIN_SRC elisp +(setq org-src-fontify-natively t + org-html-include-timestamps nil + org-html-toplevel-hlevel 2 + org-html-htmlize-output-type 'css + org-export-with-section-numbers nil + org-export-with-sub-superscripts nil + org-export-htmlize-output-type 'css) +#+END_SRC + +Make sure to use the proper template when exporting to ODT: + +#+BEGIN_SRC elisp +(setq org-odt-styles-file "~/Documents/Templates/mssola.ott") +#+END_SRC + +Sometimes it's useful to export to LaTeX. That is, when you are simply writing a +quick document that will end up being converted into LaTeX and finally into PDF: + +#+BEGIN_SRC elisp +(require 'ox-latex) + +(unless (boundp 'org-latex-classes) + (setq org-latex-classes nil)) + +(add-to-list 'org-latex-classes + '("article" + "\\documentclass{article}" + ("\\section{%s}" . "\\section*{%s}") + ("\\subsection{%s}" . "\\subsection*{%s}") + ("\\subsubsection{%s}" . "\\subsubsection*{%s}") + ("\\paragraph{%s}" . "\\paragraph*{%s}") + ("\\subparagraph{%s}" . "\\subparagraph*{%s}"))) +#+END_SRC + +You can also export org documents to man pages. In order to do so, you have to +perform this first: + +#+BEGIN_SRC elisp +(require 'ox-man) +#+END_SRC + +Setup a function to toggle =org-publish-current-file= on save: + +#+BEGIN_SRC elisp +(defun toggle-org-publish-current-file-on-save () + (interactive) + (if (memq 'org-publish-current-file after-save-hook) + (progn + (remove-hook 'after-save-hook 'org-publish-current-file t) + (message "Disabled org-publish-current-file for current buffer...")) + (add-hook 'after-save-hook 'org-publish-current-file nil t) + (message "Enabled org-publish-current-file for current buffer..."))) +#+END_SRC + +Also hide the "Footnotes: " title on footnotes: + +#+BEGIN_SRC elisp +(setq org-html-footnotes-section "<div id=\"footnotes\"> +<!-- Hack: %s --> +<div id=\"text-footnotes\"> +%s +</div> +</div>") +#+END_SRC + +Last but not least, some handy shortcuts: + +#+BEGIN_SRC elisp +(with-eval-after-load "org" + (define-key org-mode-map (kbd "<f1>") 'org-latex-export-to-pdf) + (define-key org-mode-map (kbd "<f2>") 'org-odt-export-to-odt)) +#+END_SRC + +** Agenda + +Custom commands for =org-agenda=. + +#+BEGIN_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))))) +#+END_SRC + +The prefix for the different kinds of types being used. + +#+BEGIN_SRC elisp + (setq org-agenda-prefix-format '((agenda . "%t%s") + (tags . "%c:%s") + (todo . "%c:%t%s"))) +#+END_SRC + +Set up a key binding for org-agenda. + +#+BEGIN_SRC elisp +(global-set-key (kbd "C-c a") 'org-agenda) +#+END_SRC + +** Capture + +Set the default notes file and the key binding. + +#+BEGIN_SRC elisp +(setq org-default-notes-file (concat org-directory "/notes.org")) +(define-key global-map "\C-cc" 'org-capture) +#+END_SRC + +And finally set =org-capture-templates=. + +#+BEGIN_SRC elisp +(setq org-capture-templates + `(("t" "todo" entry (file "") "* TODO %?\n%U\n") + ("i" "idea" entry (file "") "* %? :IDEA:\n%U\n%a\n"))) +#+END_SRC + +** Publish project + +I write blog posts with org-mode. Here's the trick: + +#+BEGIN_SRC elisp +(setq org-publish-project-alist + '(("org-mssola" + ;; Path to your org files. + :base-directory "~/src/mssola/jo/org/" + :base-extension "org" + + ;; Path to your Jekyll project. + :publishing-directory "~/src/mssola/jo/_i18n" + :recursive t + :publishing-function org-html-publish-to-html + :headline-levels 4 + :html-extension "html" + :body-only t ;; Only export section between <body> </body> + ) + + ("mssola" :components ("org-mssola")))) +#+END_SRC + +** Other + +Insert a <kbd></kbd> value in org mode. See this [[http://emacs.stackexchange.com/questions/2206/i-want-to-have-the-kbd-tags-for-my-blog-written-in-org-mode][StackExchange answer]]. + +#+BEGIN_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) +#+END_SRC + +Define profiles. + +#+BEGIN_SRC elisp +;; Variables + +(defvar mssola-org-profiles + '(("minimal" . mssola-org-minimal) + ("monthly" . mssola-org-monthly)) + "Defined profiles for organization matters.") + +(defvar mssola-org-default-profile "minimal" + "The default profile for mssola-org.") + +;; Profiles + +(defun mssola-org-minimal () + "Load a minimal set of files." + (find-file (concat (file-name-as-directory org-directory) "setmana.org"))) + +(defun mssola-org-monthly () + "Load a set of files useful for monthly planning." + (find-file (concat (file-name-as-directory org-directory) "setmana.org")) + (split-window-right) + (windmove-right) + (find-file (concat (file-name-as-directory org-directory) "any.org")) + (windmove-left)) + +;; Functions + +(defun mssola-org (&optional profile) + "Setup a frame for organizational matters. +PROFILE is the profile to be picked when given. If it's not given, then the +user will be prompted to provide it." + (interactive) + + (delete-other-windows) + (unless profile + (setq profile (completing-read "Give me the profile: " + (mapcar 'car mssola-org-profiles) nil t))) + (funcall (cdr (assoc profile mssola-org-profiles)))) + +(defun mssola-org-default () + "Setup a frame for organizational matters given a default profile has been set." + (interactive) + + (mssola-org mssola-org-default-profile)) + +(define-key global-map (kbd "C-c C-o") #'mssola-org-default) +(define-key global-map (kbd "C-c M-o") #'mssola-org) +#+END_SRC + +** TODO shortcut for making an org link, and transforming a link into a proper org link +** TODO make it work with evil +** TODO Proper keybindings for quick access. + +** TODO shortcuts for stuff like: create something urgent for today + +* IRC + +I'm using [[https://www.gnu.org/software/emacs/manual/html_mono/erc.html][ERC]] for IRC. + +#+BEGIN_SRC elisp +(use-package erc + :config +#+END_SRC + +First of all, let's add some basic modules. + +#+BEGIN_SRC elisp + (dolist (mod '(autojoin track truncate)) + (add-to-list 'erc-modules mod)) +#+END_SRC + +Setting up basic stuff. + +#+BEGIN_SRC elisp + (setq erc-hide-list '("PART") + erc-prompt (lambda () (concat (buffer-name) ">")) + erc-track-exclude-types '("JOIN" "NICK" "PART" "QUIT" "MODE") + erc-server-coding-system '(utf-8 . utf-8) + erc-kill-buffer-on-part t + erc-kill-queries-on-quit t + erc-kill-server-buffer-on-quit t + erc-fill-column 100 + erc-fill-prefix "" + erc-timestamp-format "[%H:%M] " + erc-insert-timestamp-function 'erc-insert-timestamp-left + erc-insert-away-timestamp-function 'erc-insert-timestamp-left + erc-hide-timestamps nil + erc-whowas-on-nosuchnick t + erc-public-away-p nil + erc-echo-notice-always-hook '(erc-echo-notice-in-minibuffer) + erc-auto-set-away nil + erc-autoaway-message "%i seconds out..." + erc-away-nickname "msabate" + erc-enable-logging t + erc-query-on-unjoined-chan-privmsg t) +#+END_SRC + +Let's log messages whenever I receive/send them. The other option is to only do +that on =/quit= or =/part=, but it's better to be safe than sorry. + +#+BEGIN_SRC elisp + (require 'erc-log) + (erc-log-enable) + + (setq erc-log-channels-directory "~/.emacs.d/erc" + erc-save-buffer-on-part nil + erc-save-queries-on-quit nil + erc-log-write-after-send t + erc-log-write-after-insert t) +#+END_SRC + +Servers and channels to auto-join. + +#+BEGIN_SRC elisp + (setq erc-autojoin-channels-alist + '(("irc.freenode.net" "#gnu" "#emacs") + ("irc.nue.suse.com" "#suse" "#docker"))) +#+END_SRC + +Use the =erc-hl-nicks= package, so highlight support for nicknames is better. + +#+BEGIN_SRC elisp +(use-package erc-hl-nicks + :ensure t + :init + (with-eval-after-load 'erc + (add-to-list 'erc-modules 'hl-nicks))) +#+END_SRC + +I want to have a desktop notification whenever someone mentions my name. For +this, I'm using the =erc-notifications= package which is built in ERC since +GNU Emacs 24.3. + +#+BEGIN_SRC elisp +(with-eval-after-load 'erc + (setq erc-notifications-icon + (concat + "/usr/share/emacs/" + (format "%s.%s" emacs-major-version emacs-minor-version) + "/etc/images/icons/hicolor/24x24/apps/emacs.png")) + (add-to-list 'erc-modules 'notifications)) +#+END_SRC + +At this point, we can safely update all the loaded ERC modules. + +#+BEGIN_SRC elisp + (add-hook 'erc-connect-pre-hook + (lambda (x) (erc-update-modules))) +#+END_SRC + +Start some modules which won't do it by default. Moreover, according to the [[https://www.emacswiki.org/emacs/ErcFilling][wiki]] +=auto-fill-mode= should be disabled if I'm using =erc-fill-mode=. + +#+BEGIN_SRC elisp + (add-hook 'erc-mode-hook + '(lambda () + (erc-track-mode t) + (auto-fill-mode -1) + (erc-log-mode 1) + (erc-autojoin-mode 1))) +#+END_SRC + +And now define a function to connect to both IRC servers. + +#+BEGIN_SRC elisp + (defun mssola-erc () + "Join pre-specified servers and channels." + + (interactive) + + (erc :server "irc.freenode.net" :port 6667 :nick "mssola") + (erc-tls :server "irc.nue.suse.com" :port 6697 :nick "mssola")) + + (global-set-key (kbd "C-c i") 'mssola-erc)) +#+END_SRC + +* Languages + +** General + +First of all, define a function that identifies some warning keywords +(e.g. TODO). This function can then be applied to the proper mode. + +#+BEGIN_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\\|BUG\\)" + 1 font-lock-warning-face prepend)))) +#+END_SRC + +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. + +#+BEGIN_SRC elisp + (add-hook 'text-mode-hook + (lambda () + (flyspell-mode 1) + (wc-mode 1))) +#+END_SRC + +** Shell + +#+BEGIN_SRC elisp +(use-package bats-mode + :ensure t) +#+END_SRC + +** Lisp + +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=. + +#+BEGIN_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 "ξ"))) +#+END_SRC + +Configure =ielm= with the proper ELisp utilities: + +#+BEGIN_SRC elisp +(use-package ielm + :config + (add-hook 'ielm-mode-hook #'eldoc-mode) + (add-hook 'ielm-mode-hook #'rainbow-delimiters-mode) + (global-set-key (kbd "C-x i e") 'ielm) + (global-set-key (kbd "C-x i l") 'ielm)) +#+END_SRC + +** C and C++ + +C and C++ only require the =warnings-mode-hook= function, spell checking for the +comments and the usage of tabs instead of spaces. + +#+BEGIN_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))) +#+END_SRC + +CMake for y'all. + +#+BEGIN_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))) +#+END_SRC + +** Ruby + +Include warning keywords in Ruby and do not automatically include the encoding +magic comment: + +#+BEGIN_SRC elisp +(use-package ruby-mode + :config + (setq ruby-insert-encoding-magic-comment nil) + (add-hook 'ruby-mode-hook #'subword-mode) + (add-hook 'ruby-mode-hook 'warnings-mode-hook)) +#+END_SRC + +And give me an IRB instance: + +#+BEGIN_SRC elisp +(use-package inf-ruby + :ensure t + :config + (add-hook 'ruby-mode-hook #'inf-ruby-minor-mode) + (global-set-key (kbd "C-x i r") 'inf-ruby)) +#+END_SRC + +** Go + +And now my Go configuration. This includes stuff like the usage of =goimports=, +=gofmt= on save, among many other useful things. + +#+BEGIN_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) + + (setq indent-tabs-mode t) + (flyspell-prog-mode) + + ; eldoc support + (use-package go-eldoc + :ensure t + :config + (require 'go-eldoc))) + + ;; 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)) +#+END_SRC + +** Python + +Install =elpy= as the environment for Python support: + +#+BEGIN_SRC elisp +(use-package elpy + :ensure t + :config + (advice-add 'python-mode :before 'elpy-enable) + (when (require 'flycheck nil t) + (setq elpy-modules (delq 'elpy-module-flymake elpy-modules)) + (add-hook 'elpy-mode-hook 'flycheck-mode))) +#+END_SRC + +Automatically use =autopep8= on save: + +#+BEGIN_SRC elisp +(use-package py-autopep8 + :ensure t + :config + (add-hook 'elpy-mode-hook 'py-autopep8-enable-on-save)) +#+END_SRC + +** Rust + +Install =rust-mode= and add some hooks to make it friendlier. + +#+BEGIN_SRC elisp +(use-package rust-mode + :ensure t + :config + + (add-hook 'before-save-hook + #'(lambda () + (when (eq major-mode 'rust-mode) + (rust-format-buffer)))) + + (use-package flycheck-rust + :ensure t + :config + + (add-hook 'flycheck-mode-hook #'flycheck-rust-setup))) +#+END_SRC + +** Tabs vs spaces + +Tabs or spaces? [[https://www.emacswiki.org/emacs/TabsSpacesBoth][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). + +#+BEGIN_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)) +#+END_SRC + +** Inferior markup languages. + +#+BEGIN_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)) +#+END_SRC + +** Web-related stuff. + +Slim and SCSS. + +#+BEGIN_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 coffee-mode + :ensure t) +#+END_SRC + +Languages specific for backend code like PHP, and =web-mode=, which provides a +bundle of features which are interesting for web-related stuff. + +#+BEGIN_SRC elisp +(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))) + + (add-hook 'js-mode-hook + (lambda () + (setq js-indent-level 2) + (rainbow-delimiters-mode 1))) + + (use-package vue-mode + :ensure t + :config + (setq mmm-submode-decoration-level 0)) + + ; React: treat it as a derived mode. Idea taken from spacemacs. + (define-derived-mode react-mode web-mode "react") + (add-to-list 'auto-mode-alist '("\\.jsx\\'" . react-mode)) + (add-to-list 'auto-mode-alist '("\\.react.js\\'" . react-mode)) + + (add-hook 'react-mode-hook + (lambda () + (yas-activate-extra-mode 'js-mode) + (web-mode-set-content-type "jsx") + (setq-local web-mode-enable-auto-quoting nil) + (rainbow-delimiters 1)))) +#+END_SRC + +The =json-reformat= package provides functions for reformatting JSON strings. It +happens from time to time that I have to read JSON output from responses, and it +can be frustrating without proper formatting. + +#+BEGIN_SRC elisp +(use-package json-reformat + :ensure t) +#+END_SRC + +** Devops + +Highlighting for Dockerfiles. + +#+BEGIN_SRC elisp + (use-package dockerfile-mode + :ensure t + :config + + (add-to-list 'auto-mode-alist '("Dockerfile" . dockerfile-mode))) +#+END_SRC + +Stuff from Hashicorp like terraform and HCL. + +#+BEGIN_SRC elisp +(use-package terraform-mode + :ensure t + :config + + (terraform-format-on-save-mode)) + +(use-package hcl-mode + :ensure t) +#+END_SRC + +#+BEGIN_SRC elisp +(use-package salt-mode + :ensure t) +#+END_SRC + +** Others + +The [[https;//github.com/mssola/soria][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). + +#+BEGIN_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)) +#+END_SRC + +** LaTeX + +Let's use [[https://www.gnu.org/software/auctex/][Auctex]] for LaTeX files. + +#+BEGIN_SRC elisp +(use-package tex + :ensure auctex + :config + (setq TeX-auto-save t) + (setq TeX-parse-self t) + (setq-default TeX-master nil)) +#+END_SRC + +* WoMan + +=WoMan= is a package that is included inside of GNU Emacs by default, and that +takes care of visualizing man pages. Let's properly setup Evil mode for this: + +#+BEGIN_SRC elisp +(with-eval-after-load "evil" + (evil-set-initial-state 'woman-mode 'normal) + (evil-define-key 'normal woman-mode-map + "J" 'Man-next-section + "K" 'Man-previous-section + "\C-h" 'evil-window-left + "\C-l" 'evil-window-right + "\C-j" 'evil-window-down + "\C-k" 'evil-window-up + "\C-u" 'evil-scroll-up)) +#+END_SRC + +Finally, I want man pages to fill all the frame: + +#+BEGIN_SRC elisp +(setq woman-fill-frame t) +#+END_SRC + +Start =WoMan= as a subcommand of the help command: + +#+BEGIN_SRC elisp +(define-key 'help-command (kbd "M-m") #'woman) +#+END_SRC + +* Misc + +Install a set of useful functions from [[https://github.com/bbatsov][@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). + +#+BEGIN_SRC elisp +(use-package crux + :ensure t + :config + + (global-set-key (kbd "C-c d") 'crux-delete-file-and-buffer) + (global-set-key (kbd "C-c r") 'crux-rename-file-and-buffer) + (global-set-key (kbd "C-c n") 'crux-cleanup-buffer-or-region) + (global-set-key (kbd "C-<backspace>") 'crux-kill-line-backwards) + (global-set-key [remap move-beginning-of-line] 'crux-move-beginning-of-line) + (global-set-key (kbd "C-c o") 'crux-open-with)) +#+END_SRC + +** Emojis + +Display emojis in buffer! + +#+BEGIN_SRC elisp +(use-package emojify + :ensure t + :config + + (add-hook 'after-init-hook #'global-emojify-mode) + (setq emojify-composed-text-p nil) + (setq emojify-emoji-styles '(unicode github))) +#+END_SRC + +* Credits + +I've built this file by simply scavenging from other people's emacs.d/dotfiles +repositories. I have taken lots of pieces from here and there, but most notably: + +- [[https://github.com/ereslibre/dotfiles][@ereslibre]] +- [[https://github.com/dmacvicar/dotfiles][@dmacvicar]] +- [[https://github.com/bbatsov/emacs.d][@bbatsov]] +- [[https://github.com/aaronbieber/dotfiles][@aaronbieber]] +- [[https://github.com/purcell/emacs.d][@purcell]] +- [[https://github.com/sachac/.emacs.d][@sachac]] ([[http://pages.sachachua.com/.emacs.d/Sacha.html][HTML version]]) +- [[https://github.com/larstvei/dot-emacs][@larstvei]] + +* License + +#+BEGIN_SRC text :tangle no + Copyright (C) 2014-2019 Miquel Sabaté Solà <mikisabate@gmail.com> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +#+END_SRC |
