Skip to content

Latest commit

 

History

History
2282 lines (1873 loc) · 82.3 KB

init.org

File metadata and controls

2282 lines (1873 loc) · 82.3 KB

Emacs literate configuration

Table of Contents

Emacs packages

Emacs now provide an entire ecosystem of packages, in various repositories, and handles them through the package utility. Emacs packages can come from different repositories. We only activate the official GNU repository, as well as MELPA-stable, MELPA and ORG:

(setq package-archives 
      '(("gnu" . "https://elpa.gnu.org/packages/")
        ("non-gnu" . "https://elpa.nongnu.org/nongnu/")
        ("melpa-stable" . "https://stable.melpa.org/packages/")
        ("melpa" . "https://melpa.org/packages/")
        ;; ("org" . "https://orgmode.org/elpa/")
        )
      package-archive-priorities
      '(
        ("non-gnu"      . 20)
        ("melpa"        . 10)
        ("gnu"          . 5)
        ("melpa-stable" . 0)
        ))

We now load it and make sure Emacs does not load it automatically a second time at startup, after all configuration is loaded:

(package-initialize)
(setq package-enable-at-startup nil)

We can now automatically install the packages that are not in Debian repositories. We first list these packages:

(setq package-list '(
                     anzu               ; current match and total matches information in the mode-line in various search modes.
                     auctex             ; Write and format TeX files
                     auctex-latexmk     ; Add LatexMk support to AUCTeX
                     auto-dark          ; Switch from 2 themes following Gnome dark mode switch
                     beginend           ; Redefine ~M-<~ (=beginning-of-buffer=) and ~M->~ (=end-of-buffer=)
                     bonjourmadame      ; Say "Hello ma'am!"
                     buffer-move        ; Move buffers to the left/top/right/bottom
                     centered-window    ; Center the text of the window (if only one window)
                     circadian          ; Switch from 2 themes following sunset/sunrise
                     ;; col-highlight    ; EmacsWiki not on MELPA anymore
                     company            ; "Complete anything"
                     company-auctex     ; Company support for AUCTeX
                     company-reftex     ; Company support for RefTeX
                     company-math       ; Company support for math mode
                     counsel            ; Various completion functions using Ivy
                     counsel-world-clock ; Display world clock using Ivy
                     dimmer             ; Visually highlight the selected buffer
                     dired-narrow       ; Filter the listing produced by Dired
                     dired-quick-sort   ; Persistent quick sorting of Dired buffers
                     dired-single       ; Dired in a single buffer
                     eshell-git-prompt  ; Themes for Emacs Shell (Eshell) prompt
                     ess                ; Emacs Speaks Statistics
                     ess-smart-equals   ; Flexible, context-sensitive assignment for R
                     ess-view-data      ; Tidyverse-like view and manipulate data in R
                     expand-region      ; Increase selected region by semantic units
                     flycheck           ; On-the-fly syntax checking
                     flx                ; Fuzzy matching
                     forge              ; Access Git forges (GitHub/GitLab) from Magit
                     format-sql         ; Reformat SQL code using sqlformat or pgformatter 
                     highlight          ; Highlighting commands
                     highlight-indent-guides ; Highlight indentation levels
                     imenu-list         ; Show imenu entries in a separate buffer 
                     ivy-bibtex         ; Search and manage bibliographies using Ivy
                     ivy-prescient      ; Simple but effective sorting and filtering
                     ivy-rich           ; More friendly interface for Ivy
                     magit              ; Text-based user interface to Git
                     magit-gitflow      ; GitFlow plugin for Magit
                     markdown-mode      ; Edit Markdown-formatted text
                     markdown-toc       ; Generate a TOC in Markdown file 
                     move-text          ; Move current line or region up or down 
                     multiple-cursors   ; Multiple cursors
                     neotree            ; File tree plugin
                     nord-theme         ; Arctic, north-bluish clean and elegant theme
                     org-bullets        ; UTF-8 bullets for Org-mode 
                     org-contrib        ; Org-mode contributed packages
                     pandoc-mode        ; Interact with Pandoc
                     pdf-tools          ; Support library for PDF files
                     polymode           ; Multiple major modes inside a single buffer
                     poly-R             ; R files support for Polymode
                     poly-markdown      ; Markdown files support for Polymode
                     poly-noweb         ; Noweb files support for Polymode
                     poly-org           ; Org files support for Polymode
                     powerthesaurus     ; Finding synonyms, antonyms, and related terms
                     redacted           ; Hide current text
                     smex               ; Smart M-x enhancement 
                     sql-indent         ; Syntax based indentation for SQL files
                     sqlite3            ; SQLite3 API
                     sqlup-mode         ; Upcase SQL keyword and functions 
                     string-inflection  ; underscore -> UPCASE -> CamelCase conversion of names 
                     tango-plus-theme   ; Color theme loosely based on the tango palette
                     toc-org            ; Up-to-date table of contents in Org files
                     web-mode           ; Editing web templates
                     writeroom-mode     ; Distraction-free writing
                     xkcd               ; Read xkcd
                     yaml-mode          ; Edit files in the YAML data serialization format
                     ))
;; Markdown-mode from MELPA, and not MELPA stable
;; (setq package-pinned-packages
;;       '((markdown-mode         . "melpa")
;;         (ivy-bibtex            . "melpa")))

Then fetch the list of packages available and install the missing packages:

(unless package-archive-contents
  (package-refresh-contents))
(dolist (package package-list)
  (unless (package-installed-p package)
    (package-install package)))

From now on, given that all packages are installed with a purpose, Emacs will silently accept redefinitions of functions by packages:

(setq ad-redefinition-action 'accept)

Additional functions

Additional Lisp functions (.el files) are placed in the functions folder of the Emacs profile:

(add-to-list 'load-path (concat user-emacs-directory "functions"))

Interface

Disable the splash screen, and open a specific note on startup:

(setq inhibit-startup-screen t)

Menu disabled by default. Show it with C-S-F1:

(global-set-key [(ctrl shift f1)] 'menu-bar-mode)

Define a my-cache-dir (~/.emacs.d/cache/) folder for later use (all caches, auto-saves, etc.).

(setq my-cache-dir (concat user-emacs-directory "cache/"))

Color theme

With the help of the package circadian, Emacs can switch themes based on sunrise and sunset. I will use Tango Plus during the day, and Nord Emacs at night (with brightness of comments at 20%). Coordinates are for Southern France. **Note:** There is currently a bug with the switch to Nord theme at night…

(setq nord-comment-brightness 20)
;; (load-theme 'nord t)
;; Montpellier
(setq calendar-location-name "Montpellier, FR")
(setq calendar-latitude 43.6108)
(setq calendar-longitude 3.8767)
(setq circadian-themes '((:sunrise . tango-plus)
                         (:sunset  . nord)))
;; (setq circadian-themes '(
;;                          (:sunrise . doom-one-light)
;;                          (:sunset  . doom-one)
;;                          ;; ("11:37" . doom-one-light)
;;                          ;; ("11:38" . doom-one)
;;                          ))
(add-hook 'circadian-before-load-theme-hook
          #'(lambda (theme)
              (setq custom-face-attributes '())))
(circadian-setup)

With auto-dark, Emacs can switch to dark mode when the system itself switches to dark mode (consequently, dark mode needs to be enabled in the system; works for GNOME). I will use Tango Plus during the day, and Nord Emacs at night (with brightness of comments at 20%).

(setq nord-comment-brightness 20)
(setq auto-dark-light-theme 'tango-plus)
(setq auto-dark-dark-theme 'nord)
(auto-dark-mode t)

Frames

Frames (generally called windows) have a title instead of emacs25@<computer>:

(setq frame-title-format '(buffer-file-name "Emacs: %b (%f)" "Emacs: %b"))

Windows

Emacs will split horizontally preferably, instead of vertically:

;; (setq split-height-threshold 20)
;; (setq split-width-threshold 100)

(defun my-split-window-sensibly (&optional window)
  (let ((window (or window (selected-window))))
    (or (and (window-splittable-p window t)
             ;; Split window horizontally.
             (with-selected-window window
               (split-window-right)))
        (and (window-splittable-p window)
             ;; Split window vertically.
             (with-selected-window window
               (split-window-below)))
        (and (eq window (frame-root-window (window-frame window)))
             (not (window-minibuffer-p window))
             ;; If WINDOW is the only window on its frame and is not the
             ;; minibuffer window, try to split it horizontally disregarding
             ;; the value of `split-width-threshold'.
             (let ((split-width-threshold 0))
               (when (window-splittable-p window t)
                 (with-selected-window window
                   (split-window-right))))))))

(setq split-window-preferred-function 'my-split-window-sensibly)

Visually highlight selected buffer, by dimming other buffers (package dimmer):

(require 'dimmer)
(setq dimmer-fraction 0.25)
(dimmer-configure-which-key)
(dimmer-configure-helm)
(dimmer-mode t)

Use M-<arrows> to move between windows (package windmove, built in Emacs; see Org section to remove conflicts with Org):

(windmove-default-keybindings 'meta)

Next window with C-~ (key above TAB):

(global-set-key [C-dead-grave] 'other-window)

Swap buffers with buffer-move (C-x <arrows>):

(global-set-key (kbd "C-x <up>")     'buf-move-up)
(global-set-key (kbd "C-x <down>")   'buf-move-down)
(global-set-key (kbd "C-x <left>")   'buf-move-left)
(global-set-key (kbd "C-x <right>")  'buf-move-right)

Mouse wheel does not accelerate:

(setq mouse-wheel-progressive-speed nil)

Scroll 2 lines from the edge:

(setq scroll-margin 2)

Prevent lateral scrolling from touchpad to beep:

(global-set-key (kbd "<mouse-7>")
                (lambda () (interactive)))
(global-set-key (kbd "<mouse-6>")
                (lambda () (interactive)))

Visible bells (flashes the frame):

(setq visible-bell t)

Ediff split horizontally instead of vertically, and keep the Ediff window in the same frame:

(setq ediff-split-window-function 'split-window-horizontally)
(setq ediff-window-setup-function 'ediff-setup-windows-plain)

Buffers

Center text (in the width of the frame) with C-x C:

(global-set-key (kbd "C-x C") 'centered-window-mode)

Empty scratch buffer:

(setq initial-scratch-message nil)

Lines soft wrapped at word boundary (with fringe indicators in the fringe column), except for Dired, Grep, and Imenu-list buffers; F10 to toggle line wrapping (activated by default):

(global-visual-line-mode 1)
(setq visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow))
(defun my-truncate-lines-enable ()
  "Disable line truncation, even in split windows."
  (let ((inhibit-message t) ; No messages in the echo area - needs emacs 25+
        message-log-max ; No messages in the *Messages* buffer
        truncate-partial-width-windows) ; No truncation in split windows
    (visual-line-mode 0)
    (toggle-truncate-lines 1)))
(add-hook 'dired-mode-hook #'my-truncate-lines-enable)
(add-hook 'grep-mode-hook #'my-truncate-lines-enable)
(add-hook 'imenu-list-major-mode-hook #'my-truncate-lines-enable)
(global-set-key [(f10)] 'toggle-truncate-lines)

Highlight current line globally (and toggle it with C-F10):

(global-hl-line-mode)
(global-set-key [(ctrl f10)] 'global-hl-line-mode)

Highlight columns (col-highlight): C-S-F10 to toggle column highlight mode.

(load-library "col-highlight")
(load-library "vline")
(global-set-key [(ctrl shift f10)] 'column-highlight-mode)

Highlight regions (highlight): F9 to highlight, C-F9 to move to the next highlighted text, C-S-F9 to unhighlight everything.

;; (set-face-attribute 'highlight nil 
;;                   :background "saddle brown")
(global-set-key [(f9)] 'hlt-highlight)
(global-set-key [(ctrl f9)] 'hlt-next-highlight)
(global-set-key [(ctrl shift f9)] 'hlt-unhighlight-region)

Highlight indentation in Programming mode (highlight-indent-guides):

(add-hook 'prog-mode-hook 'highlight-indent-guides-mode)
(setq highlight-indent-guides-method 'character) ; use 'column for more visible guides

Count words in region using C-+:

(global-set-key (kbd "C-+") 'count-words)

Kill THIS buffer with C-x k:

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

Redacted with C-S-escape; when in redacted mode, enable read-only-mode to ensure that we don’t change what we can’t read:

(global-set-key [(ctrl shift escape)] 'redacted-mode)
(add-hook 'redacted-mode-hook (lambda () (read-only-mode (if redacted-mode 1 -1))))

Navigation

Recenter with C-l starts with top, then middle, then bottom:

(setq recenter-positions '(top middle bottom))

Beginend (with Emacs 25.3) to redefine M-< (beginning-of-buffer) and M-> (end-of-buffer) so that point moves to meaningful beginning and end of buffers:

(beginend-global-mode)

Expand region with C-@ (then continue to expand by pressing @ or contract by pressing -):

(global-set-key (kbd "C-@") 'er/expand-region)

Saveplace: Go back to last position where the point was in a file (save positions in <cache>/saved-places):

(save-place-mode 1)
(setq save-place-file (concat my-cache-dir "saved-places"))

Use position registers (a sort of bookmark) with F1—F4: C-F1 to C-F4 to save a register, F1 to F4 to jump to a saved register:

(global-set-key [(f1)]
                (lambda () (interactive) (jump-to-register 1 nil)))
(global-set-key [(ctrl f1)]
                (lambda () (interactive) (point-to-register 1 nil)))
(global-set-key [(f2)]
                (lambda () (interactive) (jump-to-register 2 nil)))
(global-set-key [(ctrl f2)]
                (lambda () (interactive) (point-to-register 2 nil)))
(global-set-key [(f3)]
                (lambda () (interactive) (jump-to-register 3 nil)))
(global-set-key [(ctrl f3)]
                (lambda () (interactive) (point-to-register 3 nil)))
(global-set-key [(f4)]
                (lambda () (interactive) (jump-to-register 4 nil)))
(global-set-key [(ctrl f4)]
                (lambda () (interactive) (point-to-register 4 nil)))

Imenu lists the main parts of a document (sections, headers, etc.) to navigate interactively a long document (bound to C-M-=); we ask Imenu to stay up to date automatically [NB: counsel-mode supersedes imenu by =counsel-imenu=]:

(global-set-key (kbd "C-M-=") #'imenu)
(setq imenu-auto-rescan t)
;; (global-set-key [mouse-3] 'imenu)

Imenu-list does the same in a (right-hand) side buffer, with focus in it, loaded with ~C-F5 (\ and * fold and unfold all headers):

(setq imenu-list-focus-after-activation t)
(global-set-key [(ctrl f5)] #'imenu-list-smart-toggle)
(add-hook 'imenu-list-major-mode-hook (lambda ()
                                        (define-key imenu-list-major-mode-map (kbd "\\") #'hs-hide-all)
                                        (define-key imenu-list-major-mode-map (kbd "*") #'hs-show-all)
                                        (setq-local hs-hide-comments-when-hiding-all nil)))

Bookmarks are saved in <cache>/bookmarks, are set with C-S-F3 and listed with C-S-F4:

(setq bookmark-default-file (concat my-cache-dir "bookmarks"))
(global-set-key [(ctrl shift f3)] 'bookmark-set)
(global-set-key [(ctrl shift f4)] 'list-bookmarks)

Mode line

Add column number to the mode line:

(column-number-mode 1)

Anzu: display current match and total matches information in the mode-line, and show replacement interactively. Replace is bound to C-r, and replace using a RegExp is bound to C-M-r:

(global-anzu-mode 1)
(anzu-mode 1)
(global-set-key (kbd "C-r") 'anzu-query-replace)
(global-set-key (kbd "C-M-r") 'anzu-query-replace-regexp)

Custom mode-line, mostly simplified (shows if file modified, file name, Git branch, remote file, major mode, and position as `line:col (percent)`:

(setq-default mode-line-format '(
                                 "%e"  ; Error message about full memory
                                 mode-line-front-space
                                 "%* " ; Modified or read-only buffer
                                 ;; mode-line-frame-identification
                                 mode-line-buffer-identification
                                 "      "
                                 '(vc-mode vc-mode)
                                 "  "
                                 mode-line-remote ; Remote file?
                                 "      "
                                 ;; mode-line-modes ; This includes minor modes
                                 "%m" ; Only major mode
                                 "      "
                                 mode-line-position
                                 ;; "%l:%c (%p)" ; line number : column number (percent) 
                                 (does not work with PDF mode)
                                 mode-line-misc-info ; Not sure…
                                 mode-line-end-spaces
                                 ))

Minibuffer

Answer with y/n instead of yes/no:

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

Ivy for completion: =Ivy= comes with Counsel as dependencies; needs to install flx for better sorting. Ivy mode and Counsel mode everywhere (using ’prescient’ sorting):

(ivy-mode 1)
(counsel-mode 1)
(ivy-prescient-mode 1)

Simple customization (maximum size of 30% of screen instead of 25%; add recent files and bookmarks to ivy-switch-buffer; format counters with (xx/XX); use input with C-p; use fuzzy matching without space between letters, except for Swiper (search)):

(setq
 max-mini-window-height 0.30
 ivy-use-virtual-buffers t
 ivy-count-format "(%d/%d) "
 ivy-use-selectable-prompt t
 ivy-re-builders-alist '(
                         ;; (swiper . ivy--regex-plus)
                         (counsel-M-x . ivy--regex-fuzzy)
                         (read-file-name-internal . ivy--regex-fuzzy)
                         (t . ivy--regex-plus))
 ivy-initial-inputs-alist nil)

Ivy-resume (go back to state of last search) with C-S-s:

(global-set-key (kbd "C-S-s") 'ivy-resume)

Cycle through buffers with Ivy with C-TAB (see Magit section to remove conflicts with Magit; see Org section to remove conflicts with Org):

(global-set-key (kbd "<C-tab>") 'ivy-switch-buffer)

Use ivy-rich to add more information to Ivy results (only to switch buffer so far):

(ivy-rich-mode 1)  
(setq ivy-virtual-abbreviate 'full
      ivy-rich-switch-buffer-align-virtual-buffer t
      ivy-rich-path-style 'abbrev)

M-x (counsel-M-x with SMEX) states are saved in the <cache> folder:

(setq smex-save-file (concat my-cache-dir "smex-items"))

Use Counsel for enhanced M-x, Find File (C-x C-f or C-x C-o in other window), yank from history (C-S-y):

(setq counsel-find-file-at-point t)
(global-set-key (kbd "M-x") 'counsel-M-x)
(global-set-key (kbd "C-x C-f") 'counsel-find-file)
(global-set-key (kbd "C-x C-o") 'find-file-other-window)
(global-set-key (kbd "C-S-y") 'counsel-yank-pop)
;; (global-set-key (kbd "<f1> f") 'counsel-describe-function)
;; (global-set-key (kbd "<f1> v") 'counsel-describe-variable)
;; (global-set-key (kbd "<f1> l") 'counsel-find-library)
;; (global-set-key (kbd "<f2> i") 'counsel-info-lookup-symbol)
;; (global-set-key (kbd "<f2> u") 'counsel-unicode-char)
;; (global-set-key (kbd "C-c g") 'counsel-git)
;; (global-set-key (kbd "C-c j") 'counsel-git-grep)
;; (global-set-key (kbd "C-c k") 'counsel-ag)
;; (global-set-key (kbd "C-x l") 'counsel-locate)
;; (global-set-key (kbd "C-S-o") 'counsel-rhythmbox)

Use Swiper for enhanced search with C-s (Swiper comes with Counsel as a dependency):

(global-set-key (kbd "C-s") 'swiper)

Buffer editing

Never use Tabs when indenting, use spaces instead:

(setq-default indent-tabs-mode nil)

C-z undo:

(global-set-key (kbd "C-z") 'undo)

CUA mode used for rectangle selection with C-S-Ret; global mark with C-S-Space:

(setq cua-rectangle-mark-key (kbd "C-S-RET"))
(cua-selection-mode t)
(global-set-key [(ctrl shift return)] 'cua-set-rectangle-mark)

Electric pair mode: Automatically close double quotes, back quotes, parentheses, square brackets and curly brackets:

(electric-pair-mode 1)
(setq electric-pair-pairs '(
                            (?\" . ?\")
                            (?\` . ?\`)
                            (?\( . ?\))
                            (?\[ . ?\])
                            (?\{ . ?\})
                            ) )

Show matching parentheses and other characters (without any delay):

(setq show-paren-delay 0)
(show-paren-mode 1)

Automatically break long lines (set to 80 characters in the variable fill-column); turn it on and off with C-c q:

(setq-default fill-column 80)
(add-hook 'text-mode-hook 'turn-on-auto-fill)
(global-set-key (kbd "C-c q") 'auto-fill-mode)

Consider CamelCase as two words in programming modes:

(add-hook 'prog-mode-hook 'subword-mode)

Cycle between snake_case, lowerCamelCase and kebab-case using C-c C-u:

(global-set-key (kbd "C-c C-u") 'string-inflection-custom-cycle)
(setq string-inflection-skip-backward-when-done t)

(defun string-inflection-custom-cycle ()
  "foo_bar => fooBar => foo-bar => foo_bar"
  (interactive)
  (string-inflection-insert
   (string-inflection-custom-cycle-function (string-inflection-get-current-word))))

(fset 'string-inflection-cycle 'string-inflection-custom-cycle)

(defun string-inflection-custom-cycle-function (str)
  "foo_bar => fooBar => foo-bar => foo_bar"
  (cond
   ((string-inflection-underscore-p str)
    (string-inflection-lower-camelcase-function str))
   ((string-inflection-lower-camelcase-p str)
    (string-inflection-kebab-case-function str))
   (t
    (string-inflection-underscore-function str))))

Complete anything (company), with TAB ((kbd "TAB") for terminal; [tab] for graphical mode) to complete immediately, no delay and aggressive completion:

(add-hook 'after-init-hook 'global-company-mode)
(with-eval-after-load 'company
  (define-key company-active-map (kbd "TAB") #'company-complete-common)
  (define-key company-active-map [tab] #'company-complete-common))
(setq company-idle-delay 0
      company-echo-delay 0
      company-dabbrev-downcase nil
      company-minimum-prefix-length 2
      company-selection-wrap-around t
      company-transformers '(company-sort-by-occurrence
                             company-sort-by-backend-importance))

Multiple cursors (multiple-cursors), choices are saved in the cache folder; F11 to have multiple cursors in all lines of a region; C-F11 tries to be smart about marking everything you want (can be pressed multiple times); C-S-F11 marks the next item like the selection (use then arrows to select more/less); C-S-<left click> also set multiple cursors at mouse position:

(setq mc/list-file (concat my-cache-dir "mc-lists.el"))
(global-set-key [(f11)] 'mc/edit-lines)
(global-set-key [(ctrl f11)] 'mc/mark-all-dwim)
(global-set-key [(ctrl shift f11)] 'mc/mark-more-like-this-extended)
(global-set-key (kbd "C-S-<mouse-1>") 'mc/add-cursor-on-click)

Magnar Sveen wrote a very useful function to evaluate and directly replace a Lisp expression. For instance, evaluating (+ 1 2) replaces the expression by 3 (works in any buffer). It is bound to C-x C-y:

(load-library "sexp-eval-and-replace")
(global-set-key (kbd "C-x C-y") 'sexp-eval-and-replace)

Move line(s) up and down with M-S-up~/~M-S-down:

(global-set-key [M-S-down] 'move-text-down)
(global-set-key [M-S-up]   'move-text-up)

Sudden death! (with C-c C-d):

(global-set-key (kbd "C-c C-d") 'sudden-death)

sort-lines is case sensitive by default, which I don’t like. This makes it case insensitive (to get the default behavior back: M-x set-variable [RETURN] sort-fold-case [RETURN] nil [RETURN]):

(custom-set-variables
 '(sort-fold-case t t)
)

Spell check and syntax check

Emacs built-in spell check package is Ispell. A good approach is to use Hunspell as the spell check engine (needs to be installed), with “en_US” as the default dictionary (C-S-F12 to change dictionary). Flyspell (spell check on the fly) is enabled by default in all text files (C-F12 to toggle Flyspell), and in programming mode (only in the comments) in programming files. F12 (or middle click) opens the list of correction suggestions:

(setq ispell-program-name "hunspell"
      ispell-local-dictionary "en_US")

(add-hook 'text-mode-hook 'turn-on-flyspell)
(add-hook 'prog-mode-hook 'flyspell-prog-mode)

(global-set-key [f12] 'flyspell-correct-word-before-point)
(global-set-key [C-f12] 'flyspell-mode) ; + flyspell-buffer when on!
(global-set-key [C-S-f12] 'ispell-change-dictionary)

Syntax can be checked with the Flycheck package (need to install lintr package for R); I recommend to turn it on on demand (M-x flycheck-mode).

Thesaurus using Power Thesaurus with C-':

(global-set-key (kbd "C-'") 'powerthesaurus-lookup-word-dwim)

File saving

Default language environment is UTF-8:

(setq current-language-environment "UTF-8")

Don’t lock files and accepts simultaneous editing (no interlocking, which creates tmp lockfiles):

(setq create-lockfiles nil)

Auto-save in <cache>/save (after 10 seconds or 100 characters):

(setq
 auto-save-file-name-transforms `(("\\`/[^/]*:\\([^/]*/\\)*\\([^/]*\\)\\'"
                                   ,(concat my-cache-dir "save/\\2") t))
 auto-save-list-file-name (concat my-cache-dir "auto-save-list")
 auto-save-interval 100
 auto-save-timeout 10)

Backups in <cache>/save (a backup happens everytime a file is open, and then on each subsequent saves, except for files under version control). Copy backup files, keep a versioned (numbered) backup, and only keep the first 2 and last 2 versions of each backup:

(setq
 backup-directory-alist `((".*" . ,(concat my-cache-dir "save/")))
 backup-by-copying t
 version-control t
 kept-new-versions 2
 kept-old-versions 2
 delete-old-versions t)

List of recent files in <cache>/recentf:

(setq recentf-save-file (expand-file-name "recentf" my-cache-dir))

Abbreviations (Abbrevs) are a way to save keystrokes by expanding words into longer text. Emacs can save abbreviations in the cache directory silently:

(setq abbrev-file-name (concat my-cache-dir "abbrev_defs"))
(setq save-abbrevs 'silently)

Utilities

File manager

Dired (launched in current directory with with F6) lists directories first, use case-insensitive sorting, refreshes automatically directories, intelligently guesses where to copy (other window), and does not ask for confirmation for recursive copies and deletes. Switch to WDired mode (to ‘write’ file names) with C-F6, go to bookmarks with $, dynamically filter files and folders with / (part of Dired-narrow), and ediff two marked files with e (with dired-ediff-files):

(setq
 ls-lisp-use-insert-directory-program nil
 ls-lisp-ignore-case t
 ls-lisp-use-string-collate nil
 dired-quick-sort-suppress-setup-warning t)
(setq
 dired-listing-switches "-aBhl  --group-directories-first"
 ;; dired-omit-files "^\\.$"
 dired-auto-revert-buffer t
 dired-dwim-target t
 dired-recursive-copies (quote always)
 dired-recursive-deletes (quote always))
(global-set-key (kbd "<f6>")
                (lambda ()
                  (interactive)
                  (dired ".")))
(add-hook 'dired-mode-hook 'auto-revert-mode)
(eval-after-load "dired"
  '(progn
     (load-library "dired-ediff-files")
     (hl-line-mode)
     (define-key dired-mode-map [(ctrl f6)] #'dired-toggle-read-only)
     (define-key dired-mode-map "/" 'dired-narrow)
     (define-key dired-mode-map "e" 'dired-ediff-files)
     ))

dired-single reuses the current dired buffer to visit another directory, instead of creating a new buffer for the new directory: dired-quick-sort allows to interactively sort Dired buffers:

(eval-after-load "dired"
  '(progn
  (define-key dired-mode-map [return] 'dired-single-buffer)
  ;; (define-key dired-mode-map [mouse-1] 'dired-single-buffer-mouse) ; Does not work
  (define-key dired-mode-map [remap dired-mouse-find-file-other-window]
    #'dired-single-buffer-mouse)
  (define-key dired-mode-map "^" 'dired-single-up-directory)
  (define-key dired-mode-map [(backspace)] 'dired-single-up-directory)
  ))
(dired-quick-sort-setup)

Remove . from the list of files/folders (and be silent about it):

(setq-default dired-omit-files-p t)
(setq
 dired-omit-verbose nil
 dired-omit-files "^\\.$"
 dired-omit-extensions nil)

Use NeoTree to have a tree explorer on the side (bound to F5; turn off wrapping long lines); NeoTree uses case-insensitive sorting:

(setq neo-theme 'ascii)
(defadvice neo-buffer--get-nodes
  (after neo-buffer--get-nodes-new-sorter activate)
(setq ad-return-value
      (let ((nodes ad-return-value)
            (comparator (lambda (s1 s2) (string< (downcase s1)
                                            (downcase s2)))))
        (apply 'cons (mapcar (lambda (x) (sort (apply x (list nodes))
                                          comparator))
                             '(car cdr))))))
(global-set-key [(f5)] 'neotree-toggle)
;; (define-key neotree-mode-map (kb "RET")
;;   (neotree-make-executor
;;    :file-fn 'neo-open-file
;;    :dir-fn 'neo-open-dir))
(add-hook 'neo-after-create-hook
          #'(lambda (_)
              (with-current-buffer (get-buffer neo-buffer-name)
                (setq truncate-lines t))))

TRAMP history of connections in <cache>/tramp, make completion faster, shell history in standard location (“$HOME/.sh_history”), backups of remote files disabled, and just to be sure, version control is disabled on remote files (although VC is already disable entirely below:

(setq
 tramp-persistency-file-name (concat my-cache-dir "tramp")
 remote-file-name-inhibit-cache nil
 tramp-histfile-override nil
 )
(add-to-list 'backup-directory-alist
             (cons tramp-file-name-regexp nil))
(setq vc-ignore-dir-regexp
      (format "\\(%s\\)\\|\\(%s\\)"
              vc-ignore-dir-regexp
              tramp-file-name-regexp))

GIT

Magit is a interface to Git completely integrated to Emacs. Once installed, it pretty much works out of the box, there are just a couple of settings to make it even smoother (use Ivy to complete; links to Git-man; automatically refresh the repository’s status after file save). We also bound Magit to F8, and integrate Git-flow (magit-gitflow, started with C-f) and Forge (forge-dispatch-popup started with ') to Magit:

(load-library "magit-repository-directories")
(shell-command "git config --global status.showUntrackedFiles all") ; List files in folders
(global-set-key [(f8)] 'magit-status)
;; (setq vc-handled-backends (delq 'Git vc-handled-backends)) ; Remove Git from the list of backends handled by Emacs version control
;; (setq vc-handled-backends nil) ; Remove VC altogether
(setq
 transient-history-file (concat my-cache-dir "transient/history.el")
 magithub-dir (concat my-cache-dir "magithub/")
 magit-completing-read-function 'ivy-completing-read
 magit-view-git-manual-method 'man
 magit-refs-show-commit-count 'all)
(with-eval-after-load 'magit
  (load-library "magit-ls-files")
  ;; (setq magit-repolist-columns
  ;;       '(("Name" 25 magit-repolist-column-ident nil)
  ;;         ("Version" 25 magit-repolist-column-version nil)
  ;;         ("D" 1 magit-repolist-column-dirty nil)
  ;;         ("B<U" 3 magit-repolist-column-unpulled-from-upstream
  ;;          ((:right-align t)))
  ;;         ("B>U" 3 magit-repolist-column-unpushed-to-upstream
  ;;          ((:right-align t)))
  ;;         ("Path" 99 magit-repolist-column-path nil)))
  
  ;; (setcdr (cdr magit-repolist-columns)
  ;;         (cons '("D" 1 magit-repolist-column-dirty nil)
  ;;               (cddr magit-repolist-columns)))
  (require 'forge)
  (setq forge-database-file (expand-file-name "forge-database.sqlite" my-cache-dir))
  (define-key magit-mode-map (kbd "K") 'magit-ls-files)
  (add-hook 'after-save-hook 'magit-after-save-refresh-status))
(add-hook 'magit-mode-hook 'turn-on-magit-gitflow)
(with-eval-after-load 'magit-mode
  ;; C-tab is for ivy-switch-buffer
  (define-key magit-mode-map [C-tab] nil)
  ;; (magithub-feature-autoinject t)
  )

Shell

Emacs provide different possibilities to embed a Shell (for instance, M-x shell, M-x ansi-term), with different advantages and drawbacks. Here I setup Eshell (the Emacs shell, M-x eshell), with short names to redirect to buffers and completion that ignores case. eshell-git-prompt enables detection of Git repositories and brings a nice powerline:

(setq
 eshell-buffer-shorthand t
 eshell-cmpl-ignore-case t)
(eshell-git-prompt-use-theme 'powerline)

In Shell, use C-l to send commands directly to the subshell (useful for screen for instance):

(with-eval-after-load 'shell
  (define-key shell-mode-map (kbd "C-l")
    (lambda (seq) (interactive "k") (process-send-string nil seq))))

Finally, a function shell-xterm (C-F8) launches a shell with clearing capabilities (needed for screen):

(load-library "shell-xterm")
(global-set-key [(ctrl f8)] 'shell-xterm)

PDF

Use PDF tools to view PDF (libpoppler-glib-dev required):

(add-to-list 'auto-mode-alist '("\\.pdf" . pdf-tools-install))
(setq-default pdf-view-display-size 'fit-page) ; Start PDF in full page
(setq pdf-annot-activate-created-annotations t) ; Automatically annotate highlights
(add-hook 'pdf-view-mode-hook 
          #'(lambda ()
             (pdf-misc-size-indication-minor-mode) ; Show Top/Bot number in mode line?
             ;; (pdf-links-minor-mode)                ; Activate links
             (pdf-isearch-minor-mode)              ; Incremental search using normal isearch
             (define-key pdf-view-mode-map (kbd "h") 'pdf-view-fit-height-to-window) ; Fit height with 'h'
             (define-key pdf-view-mode-map (kbd "w") 'pdf-view-fit-width-to-window) ; Fit width with 'w'
             (define-key pdf-view-mode-map (kbd "f") 'pdf-view-fit-page-to-window) ; Fit page with 'f' DOES NOT WORK!
             ;; Conflict with Pdf-Links minor mode, which uses 'f' for link search
             (define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward) ; bound to `C-s`
             ;; (cua-mode 0) ; Turn off CUA so copy works
             (define-key pdf-view-mode-map (kbd "M-w") 'pdf-view-kill-ring-save) ; Use normal isearch
             (define-key pdf-view-mode-map (kbd "<C-home>") 'pdf-view-first-page) ; First page with C-Home
             (define-key pdf-view-mode-map (kbd "<C-end>") 'pdf-view-last-page))) ; Last page with C-End

From within a PDF, use P to fit the zoom to the page; h or H to the height, w or W to the width; g refreshes (reverts) the PDF; C-s for a regular text search; ? opens the help of PDF tools. Highlight: select text with the mouse, then C-c C-a h, annotate, then C-c C-c to commit; C-c C-a t and then mouse click to add a text note somewhere to the pdf page; C-c C-a o to strike-through text, and C-c C-a D and then click to delete an annotation. List annotations with C-c C-a l. Don’t forget to save the PDF (C-x C-s)!

Web browser

EWW

Emacs comes with a built-in web browser: EWW. Use M-x eww to run it; <backspace> goes to previous page; f opens the page in external browser (Firefox for me).

(with-eval-after-load 'eww
  (define-key eww-mode-map "f" 'eww-browse-with-external-browser)
  (define-key eww-mode-map [backspace] 'eww-back-url))

Emacs Application Framework (for Ubuntu 22.04?)

git clone –depth=1 -b master https://github.com/emacs-eaf/emacs-application-framework.git ~/.emacs.d/site-lisp/emacs-application-framework/

M-x eaf-install-and-update ???

cd emacs-application-framework chmod +x ./install-eaf.py ./install-eaf.py

(add-to-list 'load-path "~/.emacs.d/site-lisp/emacs-application-framework/")
(require 'eaf)
(require 'eaf-browser)

Major modes

YAML

YAML-mode for YAML headers/files:

(add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode))

Org

Org mode comes with its own keybindings, (which can easily conflict with other settings); RET follows links.

(global-set-key "\C-cl" 'org-store-link)
(global-set-key "\C-ca" 'org-agenda)
(global-set-key "\C-cc" 'org-capture)
(global-set-key "\C-cb" 'org-switchb)
(setq
 org-replace-disputed-keys t
 org-return-follows-link t)

Turn on indent mode, and use nice UTF-8 bullet points:

(setq org-startup-indented 1)
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))

#

#

Table of contents with Toc-Org (just add a :TOC: tag with C-c C-q in the first header, and the table of contents will be automatically updated on file save):

(add-hook 'org-mode-hook 'toc-org-enable) 

Org-babel can recognize code blocks from many different languages (Lisp, Bash, R, etc.) and provides a way to edit them in their respective mode (C-c '; and same keybinding to close). However, polymode provides an even better integration directly in the Org file. We load a few languages:

(with-eval-after-load 'org
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((css . t)
     (ditaa . t)
     (emacs-lisp . t)
     (latex . t)
     (lilypond . t)
     (org . t)
     (shell . t)
     (sql . t)
     (R . t))))

Conflict of Org with windmove (remove meaning or M-<arrows> in Org):

(with-eval-after-load 'org
  ;; C-tab is for ivy-switch-buffer
  (define-key org-mode-map (kbd "<C-tab>") nil)
  ;; Prevent Org from overriding the bindings for windmove.
  (define-key org-mode-map (kbd "M-<left>") nil)
  (define-key org-mode-map (kbd "M-<right>") nil)
  (define-key org-mode-map (kbd "M-<up>") nil)
  (define-key org-mode-map (kbd "M-<down>") nil))
;; (define-key org-agenda-mode-map (kbd "M-<up>") nil)
;; (define-key org-agenda-mode-map (kbd "M-<down>") nil)
;; (define-key org-agenda-mode-map (kbd "M-<left>") nil)
;; (define-key org-agenda-mode-map (kbd "M-<right>") nil)

;; Add replacements for the some of keybindings we just removed. It
;; looks like Org already binds C-up and C-down separately from M-{
;; and M-}, so we can't use those. Users will just have to make do
;; with C-c <up> and C-c <down> for now.
;;
;; Now for Org Agenda on the other hand, we could use C-up and
;; C-down because M-{ and M-} are bound to the same commands. But I
;; think it's best to take the same approach as before, for
;; consistency.
;; (define-key org-mode-map (kbd "C-<left>") #'org-shiftleft)
;; (define-key org-mode-map (kbd "C-<right>") #'org-shiftright)
;; (define-key org-agenda-mode-map (kbd "C-<left>") #'org-agenda-do-date-earlier)
;; (define-key org-agenda-mode-map (kbd "C-<right>") #'org-agenda-do-date-later))

Give the correct path to the Ditaa java library:

(setq org-ditaa-jar-path (expand-file-name "/usr/share/ditaa/ditaa.jar"))

Integration of TaskJuggler with org-mode, as to export projects to Gantt charts:

(require 'ox-taskjuggler)

LaTeX

The main package for LaTeX in Emacs is AUCTeX. In this configuration, AUCTeX integrates RefTeX (references), LatexMk and XeLaTeX (compilation) and PDF Tools (visualization). Note that compilation logs are not shown by default (use C-c C-l to see them, or add (setqTeX-show-compilation t) in the LaTeX-mode-hook). We start by configuring the LaTeX mode (notably RefTeX, fold LaTeX environments [F], Math mode [M], compilation as PDF [P], forward and inverse search [S]):

(setq TeX-parse-self t                ; Enable parse on load.
      TeX-auto-save t                 ; Enable parse on save.
      TeX-auto-local "/home/mathieu/.emacs.d/cache/auctex-auto"   ; Parsed information saved in cache folder
      TeX-style-local "/home/mathieu/.emacs.d/cache/auctex-style" ; Hand-generated information saved in cache folder
      TeX-source-correlate-mode t	; Forward and inverse search with Synctex
      TeX-clean-confirm nil ; Don't ask for confirmation to clean intermediary files
      reftex-plug-into-AUCTeX t       ; Plug RefTeX to AUCTeX
      reftex-default-bibliography '("/home/mathieu/.biblio.bib") ; Default bib
      TeX-auto-untabify t             ; Replace Tabs by spaces on save
      )
(add-hook 'LaTeX-mode-hook 
          (lambda ()
            (TeX-global-PDF-mode t)   ; Compile as PDF
            (add-to-list 'TeX-command-list '("XeLaTeX" "%`xelatex%(mode)%' %t" TeX-run-TeX nil t))
            (LaTeX-math-mode)         ; Math mode
            (turn-on-reftex)          ; RefTeX on
            (outline-minor-mode 1)	; Fold LaTeX sections
            (TeX-fold-mode 1)         ; Fold LaTeX environments
            ))

The compilation by LatexMk (a single call to perform all necessary LaTeX/BibTeX compilations) is performed through the auctex-latexmk package, which allows to have LatexMk as the default engine for LaTeX compilation:

(setq auctex-latexmk-inherit-TeX-PDF-mode t) ; LaTeXMk inherits PDF mode 
(auctex-latexmk-setup)
(add-hook 'TeX-mode-hook (lambda () (setq TeX-command-default "LatexMk")))

We also enable completion for LaTeX commands using Company:

(with-eval-after-load "tex"
  (company-auctex-init)
  )

Finally, we enable the use of PDF tools to visualize the resulting PDF and refresh it automatically:

(setq TeX-view-program-selection '((output-pdf "PDF Tools"))
      TeX-source-correlate-start-server t)
(add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer)

BibTeX

BibTex is managed with =bibtex-mode=, and searched through ivy-bibtex. It needs to know the location of the main bibliography (can handle several), where associated files are stored (their filename must start with the BibTeX key), and the list of potential extensions used there (PDF, DJVU, ZIP, etc.):

;; (autoload 'ivy-bibtex "ivy-bibtex" "" t)
;; ;; ivy-bibtex requires ivy's `ivy--regex-ignore-order` regex builder, which
;; ;; ignores the order of regexp tokens when searching for matching candidates.
;; ;; Add something like this to your init file:
;; (setq ivy-re-builders-alist
;;       '((ivy-bibtex . ivy--regex-ignore-order)
;;         (t . ivy--regex-plus)))
(require 'ivy-bibtex)
(setq
 ;; bibtex-completion-bibliography '("/home/mathieu/Work/Bibliography/BiblioMB.bib")
 bibtex-completion-bibliography '("/home/mathieu/.biblio.bib")
 ;; bibtex-completion-library-path '("/home/mathieu/Work/Bibliography/PDF/")
 bibtex-completion-library-path '("/home/mathieu/Public/Basille/Bibliographie/PDF")
 bibtex-completion-find-additional-pdfs t
 bibtex-completion-pdf-extension '(".pdf" ".djvu" ".ps" ".epub" ".mobi" ".zip")
 )

By default a PDF will be open in Emacs (with pdf-tools); alternatively, Evince can also be used with P:

(defun bibtex-completion-open-pdf-external (keys &optional fallback-action)
  (let ((bibtex-completion-pdf-open-function
         (lambda (fpath) (start-process "evince" "*helm-bibtex-evince*" "/usr/bin/evince" fpath))))
    (bibtex-completion-open-pdf keys fallback-action)))
(ivy-bibtex-ivify-action bibtex-completion-open-pdf-external ivy-bibtex-open-pdf-external)
(ivy-add-actions
 'ivy-bibtex
 '(("P" ivy-bibtex-open-pdf-external "Open PDF file in external viewer (if present)")))

Add keywords, journal and booktitle to fields to be searched (author, title, year, BibTeX key, and entry type by default); fields to be displayed: PDF, author, title, year, and journal/booktitle/type:

(setq bibtex-completion-additional-search-fields '(keywords journal booktitle)
      bibtex-completion-display-formats
      '(
        (article        . "${=has-pdf=:1} ${author:36} ${year:4} ${title:*} ${journal:40}")
        ;; (book           . "${=has-pdf=:1} ${author:36} ${year:4} ${title:*} Book                                    ")
        (inbook         . "${=has-pdf=:1} ${author:36} ${year:4} ${chapter:*} Book: ${title:34}")
        (incollection   . "${=has-pdf=:1} ${author:36} ${year:4} ${title:*} Book: ${booktitle:34}")
        (inproceedings  . "${=has-pdf=:1} ${author:36} ${year:4} ${title:*} Book: ${booktitle:34}")
        (t              . "${=has-pdf=:1} ${author:36} ${year:4} ${title:*} Type: ${=type=:34}")))

BibTeX file displayed in the order of the file (first entries at the top), and does not use prescient to sort:

(setq ivy-prescient-sort-commands '(:not swiper swiper-isearch ivy-switch-buffer ivy-bibtex))
(advice-add 'bibtex-completion-candidates
            :filter-return 'reverse)

Finally, ivy-bibtex is bound to C-c b:

(global-set-key (kbd "C-c b") 'ivy-bibtex)

In BibTeX mode (e.g. when opening and editing the main bibliography), new entries are created with C-c C-e, like C-c C-e C-a for articles, C-c C-e C-t for technical reports, and C-c C-e b for books. When the point is on an entry, pressing C-j moves to the next field. C-c C-c checks and cleans the entry at point (including generation of key if it does not exist, alignment, etc.). Use C-c C-q to only format the entry nicely. If necessary, use M-x bibtex-validate to clean the entire bibliography. Note that BibTex is set to keep alphabetic order of the bibliography; that requires initial sorting of the .bib file (use M-x bibtex-sort-buffer if necessary). Full documentation for BibTex mode is available here.

(defun current-date ()
  (format-time-string "%Y.%m.%d"))
(defun bibtex-add-date-owner ()
  ;; Tyler https://emacs.stackexchange.com/users/262/tyler
  ;; https://emacs.stackexchange.com/a/46339
  "Adds a timestamp and owner field to a bibtex entry.
  Checks to make sure it doesn't exist first."
  (interactive)
  (save-excursion
    (bibtex-beginning-of-entry)
    (if (assoc "timestamp" (bibtex-parse-entry))
        (message "timestamp already exists!")
      (bibtex-make-field '("timestamp" nil current-date) t nil))
    (bibtex-beginning-of-entry)
    (if (assoc "owner" (bibtex-parse-entry))
        (message "owner already exists!")
      (bibtex-make-field '("owner" nil user-login-name) t nil))
    ))
(setq
 bibtex-entry-format '(opts-or-alts required-fields numerical-fields whitespace realign unify-case sort-fields) ; Clean optional fields, remove brackets around numerical fields, remove white space, realign, unify case of entry type and fields, sort fields in predefined order
 bibtex-align-at-equal-sign t    ; Also align = sign
 bibtex-autokey-name-year-separator "_" ; Underscore between Name and Year
 bibtex-autokey-year-length 4           ; Year as YYYY
 bibtex-autokey-name-case-convert-function 'capitalize ; Name with capitale
 bibtex-autokey-titlewords 0                           ; No title
 bibtex-autokey-titleword-length 0                     ; No title
 bibtex-autokey-edit-before-use nil                    ; Don't edit before use
 bibtex-user-optional-fields '( ; Additional fields: DOI, url, date, owner, abstract
                               ("doi" "DOI for the entry")
                               ("url" "URL for the entry")
                               ("timestamp" "Time the entry was created" current-date)
                               ("owner" "Owner of the entry" user-login-name)
                               ("abstract" "Abstract for the entry"))
 )
(add-hook 'bibtex-clean-entry-hook 'bibtex-add-date-owner)
(setq biblio-cleanup-bibtex-function #'bibtex-clean-entry)
(setq bibtex-maintain-sorted-entries t)

With =biblio.el=, we can further check out query CrossRef or arXiv (using biblio-lookup, then copy and insert with c and i; C and I do the same, but additionally close the search window), or with the DOI (using doi-insert-bibtex).

Markdown

Markdown-mode is used to edit Markdown files (.md or .markdown) and is loaded automatically. We simply enable Math and a couple minor tweaks:

(setq
 markdown-command
 (concat ; Use Pandoc to convert Markdown to HTML, to produce a
         ; standalone HTML document rather than a snippet, to enable
         ; MathJax (to render LaTeX as MathML), and to use Pygments
         ; for syntax highlighting of code blocks
  "/usr/local/bin/pandoc"
  " --from=markdown --to=html"
  " --standalone --mathjax --highlight-style=pygments")
 markdown-asymmetric-header t           ; Asymetric headers (only # on the left)
 markdown-enable-math t                 ; Enable mathematical expressions (LaTeX)
 )

Additional home-made functions to deal with to-do items in Markdown (C-c C-t to insert **TODO** at beginning of the line, C-c C-d to switch between **TODO**/**DONE**, C-S-F5 to display an interactive list of to-do items in a Grep top buffer):

(add-hook 'markdown-mode-hook 
          (lambda ()
            (load-library "md-todo-library")
            (local-set-key (kbd "C-c C-t") 'md-todo)
            (local-set-key (kbd "C-c C-d") 'md-todo-done)
            (local-set-key [C-S-f5] 'md-todo-list)
            ))

ESS

ESS to use R, edit R script, edit R documentation (Roxygen) and prepare packages. R is not a prog-mode, so it needs its own settings in the ESS hook.

Load R

Use current directory as working directory

(setq ess-ask-for-ess-directory nil)

No startup message and no save on exit

(setq inferior-R-args "--quiet --no-save")

Run R dired with C-c r:

(global-set-key (kbd "C-c r") 'ess-rdired)

Layout

All R buffers (including R Dired) except code on the right side; Help, magit also on the right side; Grep sticks to the top:

(setq display-buffer-alist
      `(("*R Dired"
         (display-buffer-reuse-window display-buffer-in-side-window)
         (side . right)
         (slot . -1)
         (window-width . 0.5)
         (reusable-frames . nil))
        ("*R"
         (display-buffer-reuse-window display-buffer-in-side-window)
         (side . right)
         (slot . 1)
         (window-width . 0.5)
         (reusable-frames . nil)
         (dedicated . t))
        ("*Help"
         (display-buffer-reuse-window display-buffer-in-side-window)
         (side . right)
         (slot . -1)
         (window-width . 0.5)
         (reusable-frames . nil))
        ("magit:"
         (display-buffer-reuse-window display-buffer-in-side-window)
         (side . right)
         (slot . -1)
         (window-width . 0.5)
         (reusable-frames . nil))
        ("COMMIT_EDITMSG"
         (display-buffer-reuse-window display-buffer-in-side-window)
         (side . right)
         (slot . -1)
         (window-width . 0.5)
         (reusable-frames . nil))
        ("magit-diff:"
         (display-buffer-reuse-window display-buffer-in-side-window)
         (side . left)
         (slot . -1)
         (window-width . 0.5)
         (reusable-frames . nil))
        ("*grep"
         (display-buffer-reuse-window display-buffer-in-side-window)
         (side . top)
         (slot . -1)
         (window-height . 0.33)
         (reusable-frames . nil))
        ))

Width of R buffer automatically adjusted to window:

(setq ess-auto-width 'window)

Evaluation

When input is sent to the iESS buffer, does not wait for the process to finish, ensuring Emacs is not blocked:

(setq ess-eval-visibly 'nowait)

Evaluate complete chunk with C-c C-x:

(with-eval-after-load "ess" 
  (add-hook 'ess-mode-hook 
            (lambda ()
              (define-key ess-r-mode-map (kbd "C-c C-x")
                #'polymode-eval-chunk)
              (define-key inferior-ess-r-mode-map (kbd "C-c C-x")
                #'polymode-eval-chunk))))

Style

Try to match the style of the R parser as much as possible; Roxygen string for comments with only one pound for compatibility with RStudio:

(setq ess-style 'OWN)
(custom-set-variables
 '(ess-own-style-list
   (quote
    ((ess-indent-offset . 4)
     (ess-indent-from-lhs)
     (ess-indent-from-chain-start)
     (ess-indent-with-fancy-comments . t)
     (ess-offset-arguments . prev-line)
     (ess-offset-arguments-newline . prev-line)
     (ess-offset-block . prev-line)
     (ess-offset-continued . straight)
     (ess-align-nested-calls)
     (ess-align-arguments-in-calls)
     (ess-align-continuations-in-calls . prev-line)
     (ess-align-blocks control-flow))))
 '(ess-roxy-str "#'"))

Use the R parser (ess-indent-region-as-r), formatR (ess-indent-region-with-formatr) or styler (ess-indent-region-with-styler) to format R code. The later is bound to C-M-\ ou M-x indent-region.

(with-eval-after-load "ess" 
  (add-hook 'ess-mode-hook
            (lambda ()
            (load-library "ess-indent-region-r")
            (set (make-local-variable 'indent-region-function)
               'ess-indent-region-with-styler))))

Editing scripts

Automagically delete trailing whitespace when saving R script files:

(with-eval-after-load "ess" 
  (add-hook 'ess-mode-hook
            #'(lambda()
               (add-hook 'write-contents-functions
                         (lambda ()
                           (ess-nuke-trailing-whitespace)))
               (setq ess-nuke-trailing-whitespace-p t))))

ESS should not use IDO for completion; use company instead:

(setq ess-use-ido nil)

ESS smart equals to cycle smartly through operators with = (and includes the new pipe |> instead of magrittr’s pipe %>%):

(custom-set-variables
 '(ess-smart-equals-contexts
   '((t
      (comment)
      (string)
      ;; (arglist "=" "==" "!=" "<=" ">=" "<-" "<<-" "%>%")
      (arglist "=" "==" "!=" "<=" ">=" "<-" "<<-" "|>")
      (index "==" "!=" "%in%" "<" "<=" ">" ">=" "=")
      (conditional "==" "!=" "<" "<=" ">" ">=" "%in%")
      ;; (base "<-" "<<-" "=" "==" "!=" "<=" ">=" "->" "->>" ":=")
      (base "<-" "<<-" "=" "==" "!=" "<=" ">=" "->" ":=")
      (% "%*%" "%%" "%/%" "%in%" "%>%" "%<>%" "%o%" "%x%")
      ;; (not-% "<-" "<<-" "=" "->" "->>" "==" "!=" "<" "<=" ">" ">=" "+" "-" "*" "**" "/" "^" "&" "&&" "|" "||")
      (not-% "<-" "<<-" "|>" "=" "->" "->>" "==" "!=" "<" "<=" ">" ">=" "+" "-" "*" "**" "/" "^" "&" "&&" "|" "||")
      ;; (all "<-" "<<-" "=" "->" "->>" "==" "!=" "<" "<=" ">" ">=" "%*%" "%%" "%/%" "%in%" "%x%" "%o%" "%<>%" "%>%" "+" "-" "*" "**" "/" "^" "&" "&&" "|" "||")
      (all "<-" "<<-" "=" "|>" "->" "->>" "==" "!=" "<" "<=" ">" ">=" "%*%" "%%" "%/%" "%in%" "%x%" "%o%" "%<>%" "%>%" "+" "-" "*" "**" "/" "^" "&" "&&" "|" "||")
      ;; (t "<-" "<<-" "->" "->>" "%<>%"))
      (t "<-" "|>" "->" "%<>%" "<<-"))
     (ess-roxy-mode
      (comment "<-" "=" "==" "<<-" "->" "->>" "%<>%")))))

(with-eval-after-load 'ess-r-mode
  (require 'ess-smart-equals)
  (setq ess-smart-equals-extra-ops '(brace percent)) ; no 'paren'
  (ess-smart-equals-activate))

C-= to insert <- and then cycle between <-, |> and ->:

(setq ess-assign-list '(" <- " " |> " " -> "))
(with-eval-after-load "ess" 
  (add-hook 'ess-mode-hook 
            (lambda ()
              (define-key ess-r-mode-map (kbd "C-=") #'ess-cycle-assign)
              (define-key inferior-ess-r-mode-map (kbd "C-=") #'ess-cyle-assign))))

Change <- into , etc.:

(with-eval-after-load "ess" 
  (add-hook 'ess-mode-hook
            (lambda ()
              (prettify-symbols-mode))))

Turn on flyspell-mode for comments and strings:

(with-eval-after-load "ess" 
  (add-hook 'ess-mode-hook 
            (lambda ()
              (flyspell-prog-mode))))

Highlight indentation using ‘highlight-indent-guides’:

(with-eval-after-load "ess" 
  (add-hook 'ess-mode-hook 
            (lambda ()
            (highlight-indent-guides-mode))))

Consider CamelCase as two words:

(with-eval-after-load "ess" 
  (add-hook 'ess-mode-hook 
            (lambda ()
              (subword-mode))))

Data viewer (launch with C-c v; many operations available via ess-view-data-filter/select/slice/etc.):

(require 'ess-view-data)
(with-eval-after-load "ess" 
  (add-hook 'ess-mode-hook 
            (lambda ()
              (define-key ess-r-mode-map (kbd "C-c v")
                #'ess-view-data-print)
              (define-key inferior-ess-r-mode-map (kbd "C-c v")
                #'ess-view-data-print))))

RMarkdown and LaTex

Integration in AUCTeX menu:

(setq ess-swv-plug-into-auctex-p t)

In (R)Markdown, add a fenced R code block (C-return) or inline R code (C-S-return):

(add-hook 'markdown-mode-hook 
          (lambda ()
            (load-library "ess-rmd-library")
            (local-set-key [C-return] 'ess-rmd-fenced-r-code-block)
            (local-set-key [C-S-return] 'ess-rmd-inline-r-code)
            ))

Render RMarkdown files (using rmarkdown::render) with F7; render RMarkdown files (using bookdown::render_book) with C-F7; regular Pandoc with C-S-F7:

(with-eval-after-load 'polymode
  (define-key polymode-mode-map [(f7)] #'ess-rmd-render)
  (define-key polymode-mode-map [(ctrl f7)] #'ess-rmd-render-book)
  (define-key polymode-mode-map [(shift ctrl f7)] #'ess-md-pandoc))

Check (not active)

Syntax highlighting in Roxygen examples (removed from source code??!?):

(setq ess-roxy-fontify-examples t)

Remote R buffers

(defun ess-remote-r ()		; Associate R remote buffer to ESS buffer
  (interactive) (ess-remote nil "R"))

In an ESS inferior buffer, use C-l to send commands directly to the subshell (useful for screen for instance):

(define-key inferior-ess-mode-map (kbd "C-l")
   (lambda (seq) (interactive "k")
     (process-send-string nil seq)))

Prompt sticks to the bottom of the buffer, not editable above (is this necessary?):

(eval-after-load "comint"
 '(progn
    (define-key comint-mode-map [up]
      'comint-previous-matching-input-from-input)
    (define-key comint-mode-map [down]
      'comint-next-matching-input-from-input)
    (setq comint-move-point-for-output 'others)
    ;; somewhat extreme, almost disabling writing in *R*, *shell* buffers above prompt:
    (setq comint-scroll-to-bottom-on-input 'this)
    ))

SQL

SQL works already well out of the box. I set C-return to send a region (if selected) or the current paragraph:

(add-hook 'sql-mode-hook
          (lambda ()
            (load-library "sql-library")
            (local-set-key (kbd "<C-return>") 'sql-send-region-or-paragrap)))

However, more configuration is required to have a beautiful code and readable output. First of all, sql-indent allows to indent correctly SQL code:

(with-eval-after-load 'sql (load-library "sql-indent"))

In addition to it, SQL-up automatically corrects lower case SQL reserved names (SELECT, FROM, etc.). If necessary, it is called with C-c u on a region:

(add-hook 'sql-mode-hook 'sqlup-mode)
(add-hook 'sql-interactive-mode-hook 'sqlup-mode)
(add-hook 'sql-mode-hook
          (lambda ()
            (local-set-key (kbd "C-c u") 'sqlup-capitalize-keywords-in-region)))

To make it a step further, format-sql integrates the Python library of the same name, and allows to completely format the code in a region with C-M-] (an alternative is SQL-beautify):

(add-hook 'sql-mode-hook
          (lambda ()
            (local-set-key (kbd "C-M-]") 'format-sql-region)))

Set up default PostgreSQL credentials:

(setq sql-postgres-login-params
      '((server :default "localhost")
        (port :default 5432)
        (user :default "mathieu")
        (database :default "test")))

In the output, we first make sure that lines are not truncated (DOES NOT WORK):

(add-hook 'sql-interactive-mode-hook
          (lambda ()
            ;; (toggle-truncate-lines t)))
            (setq truncate-lines t)))

Web

(see [[https://github.com/hlissner/emacs-counsel-css][counsel-css]] for integration of CSS selectors with Ivy)

Web-mode is a major mode to edit Web files ([s]HTML, CSS, PHP, etc.). Here is a standard configuration, with auto-pairing, CSS colorization and a broad list of file extensions and engines associated to web-mode:

(add-to-list 'auto-mode-alist '("\\.htm?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.html?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.shtml?\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.css\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.php\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.phtml\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.tpl\\.php\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.[agj]sp\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.as[cp]x\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.erb\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.mustache\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.djhtml\\'" . web-mode))
(add-to-list 'auto-mode-alist '("\\.jinga\\'" . web-mode))
(setq
 web-mode-enable-auto-pairing t
 web-mode-enable-css-colorization t
 web-mode-engines-alist
 '(("php"    . "\\.phtml\\'")
   ("django" . "\\.jinja\\'")
   ("blade"  . "\\.blade\\.")))

Spell check with flyspell can be further integrated to web-mode:

(defun web-mode-flyspell-verify ()
  (let* ((f (get-text-property (- (point) 1) 'face))
         rlt)
    (cond
     ;; Check the words with these font faces, possibly.
     ;; this *blacklist* will be tweaked in next condition
     ((not (memq f '(web-mode-html-attr-value-face
                     web-mode-html-tag-face
                     web-mode-html-attr-name-face
                     web-mode-constant-face
                     web-mode-doctype-face
                     web-mode-keyword-face
                     web-mode-comment-face ;; focus on get html label right
                     web-mode-function-name-face
                     web-mode-variable-name-face
                     web-mode-css-property-name-face
                     web-mode-css-selector-face
                     web-mode-css-color-face
                     web-mode-type-face
                     web-mode-block-control-face)))
      (setq rlt t))
     ;; check attribute value under certain conditions
     ((memq f '(web-mode-html-attr-value-face))
      (save-excursion
        (search-backward-regexp "=['\"]" (line-beginning-position) t)
        (backward-char)
        (setq rlt (string-match "^\\(value\\|class\\|ng[A-Za-z0-9-]*\\)$"
                                (thing-at-point 'symbol)))))
     ;; finalize the blacklist
     (t
      (setq rlt nil)))
    rlt))
(put 'web-mode 'flyspell-mode-predicate 'web-mode-flyspell-verify)

Polymode

Polymode allows multiple major modes in the same document (e.g. R + Markdown in .Rmd files). It is setup for Markdown and LateX files with R:

(add-to-list 'auto-mode-alist '("\\.md" . poly-markdown-mode))
;; (add-to-list 'auto-mode-alist '("\\.[rR]md\\'" . poly-markdown+r-mode))
(add-to-list 'auto-mode-alist '("\\.[rR]md\\'" . poly-gfm+r-mode))
(add-to-list 'auto-mode-alist '("\\.[sS]nw\\'" . poly-noweb+r-mode))
(add-to-list 'auto-mode-alist '("\\.[rR]nw\\'" . poly-noweb+r-mode))
(add-to-list 'auto-mode-alist '("\\.org\\'" . poly-org-mode))

Navigate through chunks: C-PageUp / C-PageDown go to previous/next chunk; C-S-PageUp / C-S-PageDown go to previous/next chunk of the same type:

(with-eval-after-load 'polymode
  (define-key polymode-mode-map [(C-prior)] #'polymode-previous-chunk)
  (define-key polymode-mode-map [(C-next)] #'polymode-next-chunk)
  (define-key polymode-mode-map [(C-S-prior)] #'polymode-previous-chunk-same-type)
  (define-key polymode-mode-map [(C-S-next)] #'polymode-next-chunk-same-type))

Miscellaneous

XKCD: cache folder

(setq xkcd-cache-dir (concat my-cache-dir "xkcd"))

Start-up buffer

Opens the Start Here.md file if no file is selected, with NeoTree on the left (a little bit buggy):

(defun start-here ()
  (find-file "/home/mathieu/Public/Notes/Start Here.md")
  (neotree-toggle)
  ;; (other-window)
  )

(setq initial-buffer-choice
      (lambda ()
        (if (buffer-file-name)
            (current-buffer) ;; leave as-is
          (start-here))))

Postface

Of course, the very last part of this init.org file is the very function that enables Emacs to regenerate both init.el and init.elc files every time the init.org file is saved:

(defun tangle-init ()
  "If the current buffer is 'init.org' the code-blocks are
  tangled, and the tangled file is compiled."
  (when (equal (buffer-file-name)
               (expand-file-name (concat user-emacs-directory "init.org")))
    ;; 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)