Ducks

Introduction

To compile literate configuration setup init.el to point to this file. Make sure this file is not named `init.org` as org-babel will generate a .el file too and we don’t want it to be init

Core

This will generate a header at the top of the tangled file to indicate it is generated and is not meant to be modified directly.

;; -*- lexical-binding: t -*-
;; This file has been generated from dotemacs.org file. DO NOT EDIT.
;; Sources are available from https://github.com/shravantata/org

;; Copyright (C) 2024 Shravan Tata Ramalingasetty

;; This file 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, or (at your option)
;; any later version.

;; This file 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.

;; For a full copy of the GNU General Public License
;; see <https://www.gnu.org/licenses/>.
(defvar rougier/init-start-time (current-time) "Time when init.el was started")
(defvar rougier/section-start-time (current-time) "Time when section was started")
(defun rougier/report-time (section)
  (message "%-36s %.2fs"
           (concat section " " "section time: ")
           (float-time (time-subtract (current-time) rougier/section-start-time))))
(message "---------------------------------------------------------------")

Basic information

Set basic information such as name and contact details of the primary user

(setq user-full-name    "Shravan Tata Ramalingasetty"
      user-mail-address "shravantr@gmail.com")

Early init

This file is loaded before the package system and GUI is initialized, so in it you can customize variables that affect the package initialization process, such as package-enable-at-startup, package-load-list, and package-user-dir

;; Early initialization before running the init script
(setq
 site-run-file nil                         ; No site-wide  run-time initializations
 inhibit-default-init t                    ; No site-wide default library
 gc-cons-threshold most-positive-fixnum    ; Very large threshold for garbage collector during init
 package-enable-at-startup nil)            ; We'll use straight.el

(setq native-comp-eln-load-path
      (list (expand-file-name "eln-cache" user-emacs-directory)))

;; Reset garbage collector limit after init process has ended (8Mo)
(add-hook 'after-init-hook
          #'(lambda () (setq gc-cons-threshold (* 8 1024 1024))))

(setq package-enable-at-startup nil)

Defaults

(setq
 ;; Confirm before exiting emacs
 confirm-kill-emacs 'yes-or-no-p
 ;;  Do not echo
 inhibit-startup-echo-area-message nil
 ;; Don't describe a scratch message
 initial-scratch-message nil
 ;; Start full screen and maximized
 initial-frame-alist (quote ((fullscreen . maximized)))
 ;; start every frame maximized
 default-frame-alist (quote ((fullscreen . maximized)))
 ;; Never ding at me, ever.
 ring-bell-function 'ignore
 ;; Save existing clipboard text into the kill ring before replacing it.
 save-interprogram-paste-before-kill t
 ;; Prompts should go in the minibuffer, not in a GUI.
 use-dialog-box nil
 ;; Fix undo in commands affecting the mark.
 mark-even-if-inactive nil
 ;; Let C-k delete the whole line.
 kill-whole-line t
 ;; search should be case-sensitive by default
 case-fold-search nil
 ;; I want to close these fast, so switch to it so I can just hit 'q'
 help-window-select t
 ;; this certainly can't hurt anything
 delete-by-moving-to-trash t
 ;; keep the point in the same place while scrolling
 scroll-preserve-screen-position t
 ;; highlight error messages more aggressively
 next-error-message-highlight t
 ;; don't let the minibuffer muck up my window tiling
 read-minibuffer-restore-windows t
 ;; scope save prompts to individual projects
 save-some-buffers-default-predicate 'save-some-buffers-root
 ;; don't keep duplicate entries in kill ring
 kill-do-not-save-duplicates t
   ;;; Performance
 ;; Prefer loading newer compiled files
 load-prefer-newer t
 ;; Increase how much is read from processes in a single chunk (default is 4kb).
 read-process-output-max (* 512 1024) ; 512kb
 ;; Reduce rendering/line scan work by not rendering cursors or regions in
 ;; non-focused windows.
 highlight-nonselected-windows nil
 ;; Disable warnings from the legacy advice API. They aren't useful.
 ad-redefinition-action 'accept
 ;; Don't ping things that look like domain names.
 ffap-machine-p-known 'reject
 ;; By default, Emacs "updates" its ui more often than it needs to
 idle-update-delay 1.0
 ;; The title bar
 ns-use-proxy-icon nil
 frame-title-format nil)


(setq-default cursor-in-non-selected-windows nil)

(defvar minimal-emacs-frame-title-format "%b – Emacs"
  "Template for displaying the title bar of visible and iconified frame.")

(defvar minimal-emacs-debug nil
  "Non-nil to enable debug.")

;; Suppress compiler warnings and don't inundate users with their popups.
(setq native-comp-async-report-warnings-errors
      (or minimal-emacs-debug 'silent))
(setq native-comp-warning-on-missing-source minimal-emacs-debug)

(setq debug-on-error minimal-emacs-debug
      jka-compr-verbose minimal-emacs-debug)

(setq byte-compile-warnings minimal-emacs-debug)
(setq byte-compile-verbose minimal-emacs-debug)

(add-to-list 'default-frame-alist '(ns-transparent-titlebar . t))
(add-to-list 'default-frame-alist '(ns-appearance . light))

Welcome message

(let ((inhibit-message t))
  (message "Welcome Shravan Tata! /  Don't just get used to your tool, make it get used to you! ")
  (message (format "Initialization time: %s" (emacs-init-time))))

Package management

Setup the package management used by emacs to manage and install the necessary packages on the fly at the load time.

(message "Setting up emacs package management...")

Bootstrap straight.el

Using straight.el for package management and disable checking (for speedup).

(setq straight-check-for-modifications nil)
(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 6))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/radian-software/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

Custom paths

;; User path for saving files
;; (setq prelude-savefile-dir (expand-file-name "lisp" user-emacs-directory))

;; path to store lisp byte code
(add-to-list 'load-path
             (expand-file-name "lisp" user-emacs-directory))
;; path to store custom themes
(add-to-list 'custom-theme-load-path
             (expand-file-name "themes" user-emacs-directory))


;; store all backup and autosave files in the tmp dir
(setq backup-directory-alist
      `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
      `((".*" ,temporary-file-directory t)))

Package profiling

(setq use-package-compute-statistics t)

Packages installation

List all the packages that are required to be installed for the current configuration of emacs

(setq package-list
      '(
        ;; package configuration
        use-package                     ; A configuration macro for simplifying your .emacs
        ;; emacs
        gcmh                            ; Garbage collector magic hack
        esup                            ; Emacs profiler
        ;; editing
        crux                            ; A Collection of Ridiculously Useful eXtensions for Emacs.
        multiple-cursors                ; Edit multiple cursors
        ;; interface
        eyebrowse                       ; A simple-minded way of managing window configs
        orderless                       ; Use space-separated search terms in any order when completing with Icomplete or the default interface
        smartparens                     ; Smart parentheses
        ag                              ; faster search (requires system installation)
        ;; visual
        zone                            ; Mode to avoid zoning out
        olivetti                        ; Pleasant reading
        ;; themes
        nano-theme                      ; Nano theme from rougier
        spacemacs-theme                 ; Spacemacs theme
        catppuccin-theme                ; Catppuccin theme
        ;; navigation
        imenu-list                      ; Show imenu entries in a separate buffer
        ;; mini-buffer
        ;; helm                            ; A powerful completion and selection narrowing framework
        corfu                           ; Completion Overlay Region FUnction
        orderless                       ; orderless completion style
        vertico                         ; a performant and minimalistic vertical completion UI
        cape                            ; Cape provides Completion At Point Extensions which can be used in combination with the Corfu
        marginalia                      ; Show document of function in ==M-x=,
        consult                         ; provides various practical commands
        mini-frame                      ; Show minibuffer in child frame on read-from-minibuffer
        ;; minibuffer-header
        ;; modeline
        spaceline                       ; Spacemacs themed modeline
        ;; parsing
        tree-sitter                     ; tree-sitter
        tree-sitter-langs               ; tree-sitter-langs
        ;; versioning
        magit                           ; A Git porcelain inside Emacs
        diff-hl                         ; Highlights uncommitted changes in the gutter
        git-gutter                      ; Show git line status
        ;; history
        undo-tree                       ; Shows a tree like structure of the history
        ;; file
        dired-subtree                   ; Dired extensions
        openwith                        ; Open files appropriately using external apps
        ;; project management
        projectile                      ; Project interaction library for Emacs
        consult-projectile
        consult-project-extra           ; extra features for project management
        ;; citations
        citar                           ; Citation-related commands for org, latex, markdown
        citeproc                        ; A CSL 1.0.2 Citation Processor
        ;; orgmode
        org-bullets                     ; Shows org-mode bullets as pretty UTF-8 characters
        svg-lib                         ; svg tags and icons from rougier
        svg-tag-mode                    ; svg tags and icons from rougier
        bibtex                          ; for reference management
        org-ref                         ; Reference manager for orgmode
        ox-hugo                         ; Hugo exporter for org mode
        org-modern                      ; Modern theming for org mode language
        langtool                        ; English grammar and spelling check
        flycheck-languagetool           ; Text suggestions for english
        ;; proselint                    ; a linter for English prose
        writegood-mode                  ; Text analysis
        smog
        ;; powerthesaurus                  ; Finding synonyms, antonyms, and related terms
        ;; programming
        ;; general
        ;; lsp-mode
        guess-language                  ; Robust automatic language detection
        rainbow-delimiters              ; Adds colors to delimiters
        yasnippet                       ; yet another snippet
        yasnippet-snippets              ; snippets for many programming languages
        flycheck                        ; Modern on-the-fly syntax checking
        whitespace                      ; Mode for remove whitespaces
        ;; workflow
        snakemake-mode                  ; Major mode for snakemake files
        ;; python
        elpy                            ; Python Development Environment
        jedi                            ; Python auto-completion package
        sphinx-doc                      ; Doc pages for python
        cython-mode                     ; Cython major mode
        flycheck-cython                 ; Syntax checking for cython
        fill-column-indicator           ; Column indicator for python
        column-enforce-mode             ; Column enforcement for python
        pyenv-mode                      ; Environments for python
        pyvenv                          ; Activate virtual environments for python
        isortify                        ; For formating python imports
        ;; GL
        glsl-mode                       ; model GLSL shaders
        ;; godot
        gdscript-mode                   ; Godot programming mode
        ;; lisp
        paredit                         ; paredit
        ;; html
        htmlize                         ; Convert buffer text and decorations to HTML
        markdown-mode                   ; Major mode for Markdown-formatted text
        ;; yaml
        yaml-mode                       ; Major mode for editing yaml files
        ;; latex
        company-auctex                  ; auto-completion for latex
        ;; help
        ;; flyspell-correct-popup          ; Correcting words with flyspell via popup interface
        ;; flyspell-popup                  ; Correcting words with Flyspell in popup menus
        helpful                         ; A better help buffer
        which-key                       ; shows you possible keybindings when you type a partial keybinding
        ;; remote
        ssh                             ; ssh mode remote file connection
        ;; misc
        all-the-icons                   ; Excellent library of icons for filetypes
        all-the-icons-completion        ;
        mode-icons                      ; Icons for filetypes in modeline
        exec-path-from-shell		    ; Reflect system paths in Emacs
        ;; whisper                      ; Speech to text
        ;; AI
        gptel                           ; AI LLM interface for emacs
        ;; fun
        xkcd                            ; xkcd config
        ;; versioning
        llama                           ; requirement for magit
        )
      )

;; Exception for org to use system org version
(straight-use-package '(org :type built-in))

;; Install packages that are not yet installed
(dolist (package package-list)
  (straight-use-package package))

;; Special case for ospl-mode
(straight-use-package
 '(ospl-mode :type git :host github :repo "arrdem/ospl-mode"))

;; Special case for pdf-tools that has recently (2022) changed maintainer
(straight-use-package
 '(pdf-tools :type git :host github :repo "vedang/pdf-tools"))

;; Display org properties in the agenda buffer (modified version)
(straight-use-package
 '(org-agenda-property :type git :host github :repo "Malabarba/org-agenda-property"))

(straight-use-package
 '(shell-maker :type git :host github :repo "xenodium/chatgpt-shell" :files ("shell-maker.el")))

(straight-use-package
 '(chatgpt-shell :type git :host github :repo "xenodium/chatgpt-shell" :files ("chatgpt-shell.el")))

(straight-use-package
 '(indent-bars :type git :host github :repo "jdtsmith/indent-bars"))


(straight-use-package
 '(org-appear :type git :host github :repo "awth13/org-appear"))

(straight-use-package
 '(pinerose-emacs :type git :host github :repo "konrad1977/pinerose-emacs"))

(straight-use-package
 '(aider :type git :host github :repo "tninja/aider.el"))

Customization

Since init.el will be generated from this file, we save customization in a dedicated file.

(setq custom-file (concat user-emacs-directory "custom.el"))

(when (file-exists-p custom-file)
  (load custom-file nil t))

A GNU Emacs library to ensure environment variables inside Emacs look the same as in the user’s shell.

This sets $MANPATH, $PATH and exec-path from your shell, but only when executed in a GUI frame on OS X and Linux.

https://github.com/purcell/exec-path-from-shell

(when (memq window-system '(mac ns x))
  (exec-path-from-shell-initialize))

Encoding

We tell emacs to use UTF-8 encoding as much as possible.

(set-default-coding-systems 'utf-8)     ; Default to utf-8 encoding
(prefer-coding-system       'utf-8)     ; Add utf-8 at the front for automatic detection.
(set-terminal-coding-system 'utf-8)     ; Set coding system of terminal output
(set-keyboard-coding-system 'utf-8)     ; Set coding system for keyboard input on TERMINAL
(set-language-environment "UTF-8")      ;

Global

(add-hook 'after-init-hook #'global-flycheck-mode)

Recent files

50 Recents files with some exclusion (regex patterns).

(require 'recentf)

(setq recentf-max-menu-items 10
      recentf-max-saved-items 100
      recentf-exclude '()
      ;; disable recentf-cleanup on Emacs start, because it can cause
      ;; problems with remote files
      recentf-auto-cleanup 'never
      )

(let (message-log-max)
  (recentf-mode 1))

History

Remove text properties for kill ring entries (see https://emacs.stackexchange.com/questions/4187). This saves a lot of time when loading it.

(defun unpropertize-kill-ring ()
  (setq kill-ring (mapcar 'substring-no-properties kill-ring)))

(add-hook 'kill-emacs-hook 'unpropertize-kill-ring)

We save every possible history we can think of.

(require 'savehist)

(setq kill-ring-max 50
      history-length 50)

(setq savehist-additional-variables
      '(kill-ring
        command-history
        set-variable-value-history
        custom-variable-history
        query-replace-history
        read-expression-history
        minibuffer-history
        read-char-history
        face-name-history
        bookmark-history
        file-name-history))

 (put 'minibuffer-history         'history-length 50)
 (put 'file-name-history          'history-length 50)
 (put 'set-variable-value-history 'history-length 25)
 (put 'custom-variable-history    'history-length 25)
 (put 'query-replace-history      'history-length 25)
 (put 'read-expression-history    'history-length 25)
 (put 'read-char-history          'history-length 25)
 (put 'face-name-history          'history-length 25)
 (put 'bookmark-history           'history-length 25)

No duplicates in history

(setq history-delete-duplicates t)

Start history mode.

(let (message-log-max)
  (savehist-mode))

Server

Server start.

(require 'server)

(unless (server-running-p)
  (server-start))

Post

;; Make gc pauses faster by decreasing the threshold.
(setq gc-cons-threshold (* 2 1000 1000))

Garbage

Used by DOOM to manage garbage collection

(use-package gcmh
  :demand
  :config
  (gcmh-mode 1))

Definitions

Local function definitions to facilitate emacs setup

(defun waveparticle/langtool-autosave-hook ()
  ;; Check grammar with langtool before save
  (add-hook 'before-save-hook 'langtool-check-buffer nil 'local))

Benchmark

(rougier/report-time "Core")

Interface

(setq rougier/section-start-time (current-time))

Frame

;; Disable horizontal scroll bars in GUI
(when (fboundp 'horizontal-scroll-bar-mode)
  (horizontal-scroll-bar-mode 0))

Window

Winner mode is a global minor mode that records the changes in the window configuration

(winner-mode t)
(setq-default window-divider-default-right-width 6 ; Vertical window divider
              window-divider-default-places 'right-only
              left-margin-width 0
              right-margin-width 0
              window-combination-resize nil) ; Do not resize windows proportionally

(window-divider-mode 1)

Setup windmove to easily move between windows

(require 'windmove)

;; use command key on Mac
(windmove-default-keybindings 'super)
;; wrap around at edges
(setq windmove-wrap-around t)

Setup eyebrowse to create and interact with workspaces

(require 'eyebrowse)

;; Enable eyebrowse mode
(eyebrowse-mode t)
;; Start the new window to be as empty as possible
(setq eyebrowse-new-workspace t)
;; bind keys for easier movement between windows
(bind-key "M-1" #'eyebrowse-switch-to-window-config-1 'eyebrowse-mode-map)
(bind-key "M-2" #'eyebrowse-switch-to-window-config-2 'eyebrowse-mode-map)
(bind-key "M-3" #'eyebrowse-switch-to-window-config-3 'eyebrowse-mode-map)
(bind-key "M-4" #'eyebrowse-switch-to-window-config-4 'eyebrowse-mode-map)

Buffers

Set unique buffer names

(message "Personal config :. uniquify...")
;; meaningful names for buffers with the same name
;; from prelude
;; https://github.com/bbatsov/prelude
(require 'uniquify)
(setq uniquify-buffer-name-style 'forward)
(setq uniquify-separator "/")
(setq uniquify-after-kill-buffer-p t)    ; rename after killing uniquified
(setq uniquify-ignore-buffers-re "^\\*") ; don't muck with special buffers

Dialog

Pop up windows are needed for `display-buffer` to able to split window. For example, for magit to create a new window for writing the commit message.

(tool-bar-mode -1)                      ; remove tool bar
(scroll-bar-mode -1)                    ; remove scroll bar
(tooltip-mode -1)                       ; remove tooltips

(setq-default show-help-function nil    ; No help text
              use-file-dialog nil       ; No file dialog
              use-dialog-box nil        ; No dialog box
              ;; pop-up-windows nil        ; No pop windows
)

Specific case for OSX since menubar is desktop-wide (see emacs.stackexchange.com/questions/28121) and emacs-mac documentation.

(menu-bar-mode 1)

Key bindings

Section to remap general keybindings. Should be used sparingly!

;; Stop opening buffer list accidently
(unbind-key "C-x C-b")
;; (global-set-key (kbd "C-S-<left>")  'tabbar-backward)
;; (global-set-key (kbd "C-S-<right>") 'tabbar-forward)
;; (global-set-key (kbd "C-S-<up>")    'tabbar-backward-group)
;; (global-set-key (kbd "C-S-<down>")  'tabbar-forward-group)
;; (global-set-key (kbd "C-h")  'delete-backward-char)
;; (global-set-key (kbd "M-h")  'delete-backward-word)

Keyboard

The mode displays the key bindings following your currently entered incomplete command (a ;; prefix) in a popup.


(require 'which-key)

(which-key-mode)

Mouse

Mouse avoidance to move the mouse pointer away from the cursor


(setq-default mouse-yank-at-point t) ; Yank at point rather than pointer
'(mouse-avoidance-mode (quote animate) nil (avoid))

Mouse active in tty mode.


(unless (display-graphic-p)
  (xterm-mouse-mode 1)
  (global-set-key (kbd "<mouse-4>") #'scroll-down-line)
  (global-set-key (kbd "<mouse-5>") #'scroll-up-line))

Sound

Disable the bell (auditory or visual).

(setq-default visible-bell nil             ; No visual bell
              ring-bell-function 'ignore)  ; No bell

Scroll

Smoother scrolling

(pixel-scroll-precision-mode)
(setq-default scroll-conservatively 101       ; Avoid recentering when scrolling far
              scroll-margin 2                 ; Add a margin when scrolling vertically
              recenter-positions '(5 bottom)) ; Set re-centering positions

Clipboard

Allows system and Emacs clipboard to communicate smoothly (both ways)

(setq-default select-enable-clipboard t) ; Merge system's and Emacs' clipboard

Make sure clipboard works properly in tty mode on OSX.

(defun my/paste-from-osx ()
  (shell-command-to-string "pbpaste"))

(defun my/copy-to-osx (text &optional push)
  (let ((process-connection-type nil))
    (let ((proc (start-process "pbcopy" "*Messages*" "pbcopy")))
      (process-send-string proc text)
      (process-send-eof proc))))

(when (and (not (display-graphic-p))
           (eq system-type 'darwin))
  (setq interprogram-cut-function   #'my/copy-to-osx
        interprogram-paste-function #'my/paste-from-osx))

Help

Helpful is an alternative to the built-in Emacs help that provides much more contextual information. It is a bit slow to load so we do need load it explicitly.

(setq help-window-select t)             ; Focus new help windows when opened

(bind-key "C-h f"   #'helpful-callable) ; Look up callable
(bind-key "C-h v"   #'helpful-variable) ; Look up variable
(bind-key "C-h k"   #'helpful-key)      ; Look up key
(bind-key "C-c C-d" #'helpful-at-point) ; Look up the current symbol at point
(bind-key "C-h F"   #'helpful-function) ; Look up *F*unctions (excludes macros).
(bind-key "C-h C"   #'helpful-command)  ; Look up *C*ommands.

Widgets

;; No ugly button for checkboxes
(setq widget-image-enable nil)

Text

Shorten confirmation inputs

(setq-default
 use-short-answers t                    ; Replace yes/no prompts with y/n
 confirm-nonexistent-file-or-buffer nil ; Ok to visit non existent files
 )

Replace text when writing over a region

;; overwrite selected text
(delete-selection-mode t)

Some other defaults

;; Double-spaces after periods is morally wrong.
(setq sentence-end-double-space nil)

Typography

  • Column width increased to 88 from default 80. Black (python linter) suggests this to be a better default over the legacy 80 chars
  • Nicer and cleaner ellipsis
(setq-default
 fill-column 88                          ; Default line width increased by 8 chars
 sentence-end-double-space nil           ; Use a single space after dots
 bidi-paragraph-direction 'left-to-right ; Faster
 truncate-string-ellipsis "…"            ; Nicer ellipsis
 )
;; Nicer glyphs for continuation and wrap

(defface my-wrap-symbol-face
  '((t (:family "JetBrains Mono"
                :weight light)))
  "Specific face for wrap symbol")

(set-display-table-slot standard-display-table
                        'truncation (make-glyph-code ?… 'my-wrap-symbol-face))

(set-display-table-slot standard-display-table
                        'wrap (make-glyph-code ?↩ 'my-wrap-symbol-face))

Fix a bug on OSX in term mode & zsh (spurious “%” after each command)

(when (eq system-type 'darwin)
    (add-hook 'term-mode-hook
              (lambda ()
                (setq buffer-display-table (make-display-table)))))

Make sure underline is positionned at the very bottom.

(setq x-underline-at-descent-line nil
        x-use-underline-position-properties t
        underline-minimum-offset 10)

Project

Use projectile to navigate easily between projects

(require 'projectile)

(projectile-mode +1)

(if (eq system-type 'darwin)
    ;; Recommended keymap prefix on macOS
    (define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)
  ;; Recommended keymap prefix on Windows/Linux
  (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
  )

Use projectile to launch dired when invoked

(setq projectile-switch-project-action #'projectile-dired)

Disable projectile when using tramp to speed things

(defadvice projectile-on (around exlude-tramp activate)
  "This should disable projectile when visiting a remote file"
  (unless  (--any? (and it (file-remote-p it))
                   (list
                    (buffer-file-name)
                    list-buffers-directory
                    default-directory
                    dired-directory))
    ad-do-it))

Setup projecticle to automatically discover projects

(setq projectile-auto-discover nil)
(add-hook 'after-init-hook #'(lambda () (projectile-discover-projects-in-directory "~/projects" 7)))

Files

Filesystems, autosaves and backups

Emacs is super fond of littering filesystems with backups and autosaves, since it was built with the assumption that multiple users could be using the same Emacs instance on the same filesystem. This was valid in 1980. It is no longer the case.

(setq make-backup-files nil auto-save-default nil create-lockfiles nil)

Configure dired

(message "Personal config :. dired-subtree...")

(require 'dired-subtree)

(bind-keys :map dired-mode-map
           ("i" . dired-subtree-insert)
           (";" . dired-subtree-remove))

(when (eq system-type 'darwin)
  (setq insert-directory-program "gls" dired-use-ls-dired t)
  (setq dired-listing-switches "-al --group-directories-first")
 )

(when (eq system-type 'gnu/linux)
  (setq dired-listing-switches "-aBhl  --group-directories-first")
 )

(setq dired-kill-when-opening-new-dired-buffer t)

configure ssh for remote files

(require 'ssh)

(add-hook 'ssh-mode-hook
          (lambda ()
            (setq ssh-directory-tracking-mode t)
            (shell-dirtrack-mode t)
            (setq dirtrackp nil)))
;; tramp, for sudo access
(require 'tramp)
;; keep in mind known issues with zsh - see emacs wiki
(setq tramp-default-method "ssh")

Benchmark


(rougier/report-time "Interface")

Use ag as alternate to default search. Requires system installation

; Highlight search results when using ag
(setq ag-highlight-search t)

Visual


(setq rougier/section-start-time (current-time))

Font

Some defaults for EMACS

It’s good that Emacs supports the wide variety of file encodings it does, but UTF-8 should always, always be the default.

(set-charset-priority 'unicode)
(prefer-coding-system 'utf-8-unix)

This is the font stack we install:

  • Default font: JetBrains Mono 12pt Regular
  • Italic font: JetBrains Mono 12pt Light
  • Bold font: JetBrains Mono 12pt Bold
  • Unicode font: jetBrains Mono 14pt Light
  • Icon font: JetBrains Mono Nerd 12pt Light

┌───────────────────────────────────────────────┐ │  The quick brown fox jumps over the lazy dog │ │  The quick brown fox jumps over the lazy dog ┼─ JetBrains Mono Italic │  The quick brown fox jumps over the lazy dog ├─ JetBrains Mono Bold └─┼───────────────────────────┼─────────────────┘ JetBrains Mono Nerd JetBrains Mono

(set-face-attribute 'default nil
                    :family "JetBrains Mono"
                    :weight 'regular
                    :height 128)

(set-face-attribute 'bold nil
                    :family "JetBrains Mono"
                    :weight 'bold)

(set-face-attribute 'italic nil
                    :family "JetBrains Mono"
                    :weight 'light
                    :slant 'italic)

(set-fontset-font t 'unicode
                  (font-spec :name "Jetbrains Mono"
                             :weight 'light
                             :size 13) nil)

(set-fontset-font t '(#xe000 . #xffdd)
                  (font-spec :name "JetBrainsMono Nerd Font"
                             :size 13) nil)

Splash screen

Add a splash screen image and welcome message with some stats

(add-to-list 'load-path "~/fork/emacs-splash/")
(require 'splash-screen)

Colors

Themes

Catppuccin

(load-theme 'catppuccin :no-confirm)
(setq catppuccin-flavor 'latte);; or 'latte, 'macchiato, or 'mocha or 'frappe

;; ;; Change colors
;; (catppuccin-set-color 'base "#000000") ;; change base to #000000 for the currently active flavor
;; (catppuccin-set-color 'crust "#222222" 'frappe) ;; change crust to #222222 for frappe
;; (set-face-attribute 'region nil :background "#434C5E")
(catppuccin-reload)

Leuven

(load-theme 'leuven t)

Nord theme

(load-theme 'nord t)

Typography

(setq x-underline-at-descent-line nil
      x-use-underline-position-properties t
      underline-minimum-offset 10)

Fancy icons

Enable mode-icons

(require 'mode-icons)
(mode-icons-mode t)

Configure all-the-icons to work with required types

In order for the icons to work it is very important that you install the Resource Fonts included in this package, they are available in the fonts directory.

(all-the-icons-completion-mode)
(add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup)

Focus

Use zone mode is avoid blank staring at the screen

(require 'zone)
(zone-when-idle 30000)

Misc

;; No ugly button for checkboxes
(setq widget-image-enable nil)

Benchmark

(rougier/report-time "Visual")

Editing

(setq rougier/section-start-time (current-time))

Default mode

Default & initial mode is text.

(setq-default initial-major-mode 'text-mode   ; Initial mode is text
              default-major-mode 'text-mode)  ; Default mode is text

Visual line mode for prog and text modes

(add-hook 'text-mode-hook 'visual-line-mode)
(add-hook 'prog-mode-hook 'visual-line-mode)

Tree-sitter

(setq major-mode-remap-alist
 '((python-mode . python-ts-mode)))

;; (yaml-mode . yaml-ts-mode)
;;    (bash-mode . bash-ts-mode)
;;    (js2-mode . js-ts-mode)
;;    (typescript-mode . typescript-ts-mode)
;;    (json-mode . json-ts-mode)
;;    (css-mode . css-ts-mode)

Text

Subword movement lets us treat “EmacsIsAwesome” as three words ─“Emacs”, “Is”, and “Awesome”─ which is desirable since such naming is common among coders. Now, for example, M-f moves along each subword.

(global-subword-mode 1)
;; (diminish 'subword-mode)

Multiple cursors for editing

(require 'multiple-cursors)
(multiple-cursors-mode t)
(bind-key "C-S-c C-S-c" #'mc/edit-lines)
(bind-key "C->" #'mc/mark-next-like-this)
(bind-key "C-<" #'mc/mark-previous-like-this)
(bind-key "C-c C-<" #'mc/mark-all-like-this)
(require 'indent-bars)

;; indent-bars will not attempt stipple display, but instead use simple characters e.g. |; see an example
;; M-x version should say Carbon, not NS.
;; The NS build has partial stipple support in master, which may be released in Emacs v30.
(setq indent-bars-prefer-character t
      indent-bars-display-on-blank-lines nil
      indent-bars-width-frac 0.5
 )

;; modes to enabel indent bars by default
(add-hook 'python-mode-hook 'indent-bars-mode)
(setq indent-bars-treesit-support t
      indent-bars-treesit-wrap '((python function_definition
                                         class_definition
                                         for_statement
                                         if_statement
                                         with_statement
                                         while_statement
                                         argument_list
                                         parameters
                                         list
                                         list_comprehension
                                         dictionary
                                         dictionary_comprehension
                                         parenthesized_expression
                                         subscript))
      indent-bars-treesit-ignore-blank-lines-types '("module")
 )
(add-hook 'yaml-mode-hook 'indent-bars-mode)

whitespace-mode config

;; whitespace-mode config
(require 'whitespace)
(setq whitespace-line-column 80) ;; limit line length
(setq whitespace-style '(face tabs empty trailing lines-tail))
(add-hook 'before-save-hook 'whitespace-cleanup)
(require 'ospl-mode)

History

Setup undo-tree

(global-undo-tree-mode)
(setq undo-tree-limit 240000)

;; Prevent undo tree files from polluting your git repo
(setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))

Tabulations

No tabulation, ever.

(setq-default indent-tabs-mode nil        ; Stop using tabs to indent
              tab-always-indent 'complete ; Indent first then try completions
              tab-width 4)                ; Smaller width for tab characters

;; Let Emacs guess Python indent silently
(setq python-indent-guess-indent-offset t
      python-indent-guess-indent-offset-verbose nil)

Parenthesis

Paren mode for highlighting matching parenthesis

(require 'paren)
;; (setq show-paren-style 'expression)
(setq show-paren-style 'parenthesis)
(setq show-paren-when-point-in-periphery t)
(setq show-paren-when-point-inside-paren nil)
(show-paren-mode)
(electric-pair-mode)

Rainbow delimiters

(require 'rainbow-delimiters)
(add-hook 'prog-mode-hook #'rainbow-delimiters-mode)

Disable blinking matching paren

;; disable annoying blink-matching-paren
(setq blink-matching-paren nil)

Imenu list

Imenu setup

(require 'imenu-list)
(setq-default imenu-max-item-length 1000)
(bind-key "M-'" #'imenu-list-smart-toggle)
(setq-default imenu-auto-rescan t)
(setq imenu-list-size 0.15)
(set-default 'imenu-auto-rescan t)

;; Focus window on imenu immediately after activation
(setq imenu-list-focus-after-activation t)
;; Depth of org headings in the imenu buffer
(setq org-imenu-depth 3)

Highlight

Highlighting of the current line (native mode)

(require 'hl-line)
(global-hl-line-mode)

PDF Tools

For retina display (OSX)

;; (require 'pdf-tools)

(add-hook 'doc-view-mode-hook 'pdf-tools-install)

;; Using line numbers breaks the pdf-viewer
(add-hook 'pdf-view-mode-hook (lambda () (nlinum-mode -1)))

(setq-default pdf-view-use-scaling t
              pdf-view-use-imagemagick nil)

Benchmark

(rougier/report-time "Editing")

Minibuffer & Modeline

(setq rougier/section-start-time (current-time))

General

Display time in modeline

(display-time-mode 1)                   ; display time in minibar

Consult

We replace some of emacs functions with their consult equivalent

(require 'consult)
(setq consult-preview-key nil) ; No live preview

(bind-key "C-x C-r" #'consult-recent-file)
(bind-key "C-x h"   #'consult-outline)
(bind-key "C-x b"   #'consult-buffer)
(bind-key "C-c h"   #'consult-history)
;; (bind-key "M-:"     #'consult-complex-command)

For the consult-goto-line and consult-line commands, we define our owns with live preview (independently of the consult-preview-key)

(defun rougier/consult-line ()
  "Consult line with live preview"

  (interactive)
  (let ((consult-preview-key 'any)
        (mini-frame-resize 'grow-only)) ;; !! Important
    (consult-line)))

(bind-key "C-s" #'rougier/consult-line)

(defun rougier/consult-goto-line ()
  "Consult goto line with live preview"

  (interactive)
  (let ((consult-preview-key 'any))
    (consult-goto-line)))

(bind-key "M-g g"   #'rougier/consult-goto-line)
(bind-key "M-g M-g" #'rougier/consult-goto-line)

Extra packages for consult projects

(require 'consult-project-extra)

Enable binding with projectile (project-management)

(require 'consult-projectile)
(consult projectile)

Vertico

Vertico provides a performant and minimalistic vertical completion UI based on the default completion system but aims to be highly flexible, extensible and modular.

(require 'vertico)
;; (setq completion-styles '(basic substring partial-completion flex))
(setq vertico-resize nil        ; How to resize the Vertico minibuffer window.
      vertico-count 8           ; Maximal number of candidates to show.
      vertico-count-format nil) ; No prefix with number of entries

(vertico-mode)

Tweaking settings

(setq vertico-grid-separator
      #("  |  " 2 3 (display (space :width (1))
                             face (:background "#ECEFF1")))

      vertico-group-format
      (concat #(" " 0 1 (face vertico-group-title))
              #(" " 0 1 (face vertico-group-separator))
              #(" %s " 0 4 (face vertico-group-title))
              #(" " 0 1 (face vertico-group-separator
                          display (space :align-to (- right (-1 . right-margin) (- +1)))))))

(set-face-attribute 'vertico-group-separator nil
                    :strike-through t)
(set-face-attribute 'vertico-current nil
                    :inherit '(default shadow))
(set-face-attribute 'completions-first-difference nil
                    :inherit '(default))

Bind shift-tab for completion

(bind-key "<backtab>" #'minibuffer-complete vertico-map)

Completion-at-point and completion-in-region (see https://github.com/minad/vertico#completion-at-point-and-completion-in-region)

(setq completion-in-region-function
      (lambda (&rest args)
        (apply (if vertico-mode
                   #'consult-completion-in-region
                 #'completion--in-region)
               args)))

Prefix the current candidate (See https://github.com/minad/vertico/wiki#prefix-current-candidate-with-arrow)

(defun minibuffer-format-candidate (orig cand prefix suffix index _start)
  (let ((prefix (if (= vertico--index index)
                    "  "
                  "   ")))
    (funcall orig cand prefix suffix index _start)))

(advice-add #'vertico--format-candidate
           :around #'minibuffer-format-candidate)

See https://kristofferbalintona.me/posts/vertico-marginalia-all-the-icons-completion-and-orderless/#vertico

(defun vertico--prompt-selection ()
  "Highlight the prompt"

  (let ((inhibit-modification-hooks t))
    (set-text-properties (minibuffer-prompt-end) (point-max)
                         '(face (bold highlight)))))

See https://github.com/minad/vertico/issues/145

(defun minibuffer-vertico-setup ()

  (setq truncate-lines t)
  (setq completion-in-region-function
        (if vertico-mode
            #'consult-completion-in-region
          #'completion--in-region)))

(add-hook 'vertico-mode-hook #'minibuffer-vertico-setup)
(add-hook 'minibuffer-setup-hook #'minibuffer-vertico-setup)

Recommended settings to work with vertico for tramp-ssh remote files

(setq completion-styles '(orderless basic)
      completion-category-overrides '((file (styles basic partial-completion))))

Marginalia

Pretty straightforward.

(require 'marginalia)

(setq-default marginalia-ellipsis "…"     ; Nicer ellipsis
              marginalia-align 'right     ; right alignment
              marginalia-align-offset -1) ; one space on the right

(marginalia-mode)

Minibuffer

Miniframe

(require 'mini-frame)

(defcustom rougier/minibuffer-position 'bottom
  "Minibuffer position, one of 'top or 'bottom"
  :type '(choice (const :tag "Top"    top)
                 (const :tag "Bottom" bottom))
  :group 'nano-minibuffer)


(defun rougier/minibuffer--frame-parameters ()
  "Compute minibuffer frame size and position."

  ;; Quite precise computation to align the minibuffer and the
  ;; modeline when they are both at top position
  (let* ((edges (window-pixel-edges)) ;; (left top right bottom)
         (body-edges (window-body-pixel-edges)) ;; (left top right bottom)
         (left (nth 0 edges)) ;; Take margins into account
         (top (nth 1 edges)) ;; Drop header line
         (right (nth 2 edges)) ;; Take margins into account
         (bottom (nth 3 body-edges)) ;; Drop header line
         (left (if (eq left-fringe-width 0)
                   left
                 (- left (frame-parameter nil 'left-fringe))))
         (right (nth 2 edges))
         (right (if (eq right-fringe-width 0)
                    right
                  (+ right (frame-parameter nil 'right-fringe))))
         (border 1)
         (width (- right left (* 1 border)))

         ;; Window divider mode
         (width (- width (if (and (bound-and-true-p window-divider-mode)
                                  (or (eq window-divider-default-places 'right-only)
                                      (eq window-divider-default-places t))
                                  (window-in-direction 'right (selected-window)))
                             window-divider-default-right-width
                           0)))
         (y (- top border)))

    (append `((left-fringe . 0)
              (right-fringe . 0)
              (user-position . t)
              (foreground-color . ,(face-foreground 'highlight nil 'default))
              (background-color . ,(face-background 'highlight nil 'default)))
            (cond ((and (eq rougier/minibuffer-position 'bottom))
                   `((top . -1)
                     (left . 0)
                     (width . 1.0)
                     (child-frame-border-width . 0)
                     (internal-border-width . 0)))
                  (t
                   `((left . ,(- left border))
                     (top . ,y)

                     (width . (text-pixels . ,width))
                     (child-frame-border-width . ,border)
                     (internal-border-width . 0)))))))

  (set-face-background 'child-frame-border (face-foreground 'shadow))
  (setq mini-frame-default-height 3)
  (setq mini-frame-create-lazy t)
  (setq mini-frame-show-parameters 'rougier/minibuffer--frame-parameters)
  (setq mini-frame-ignore-commands
        '("edebug-eval-expression" debugger-eval-expression))
  (setq mini-frame-internal-border-color (face-foreground 'shadow))

  (setq mini-frame-resize-min-height 3)
  (setq mini-frame-resize t)
  ;; (setq mini-frame-resize 'grow-only)
  ;; (setq mini-frame-default-height (+ 1 vertico-count))
  ;; (setq mini-frame-resize-height (+ 1 vertico-count))
  ;; (setq mini-frame-resize nil)

Mini-frame mode OFF

;; (mini-frame-mode 1)

More a hack than a fix but the code below improve the mini-frame resize by setting position explicity. CURRENTLY INACTIVE

(defun rougier/mini-frame--resize-mini-frame (frame)
  "Resize FRAME vertically only.
This function used as value for `resize-mini-frames' variable."
  (funcall mini-frame--fit-frame-function
           frame
           mini-frame-resize-max-height
           (if (eq mini-frame-resize 'grow-only)
               (max (frame-parameter frame 'height)
                    mini-frame-resize-min-height)
             mini-frame-resize-min-height)
           ;; A max-width must be included to work around a bug in Emacs which
           ;; causes wrapping to not be taken into account in some situations
           ;; https://debbugs.gnu.org/cgi/bugreport.cgi?bug=56102
           (window-body-width)
           nil
           'vertically)

  (if (eq rougier/minibuffer-position 'top)
      (modify-frame-parameters  mini-frame-completions-frame `((top . 0)))
    (modify-frame-parameters  mini-frame-completions-frame `((top . (- 1))))))

Modeline

Get modeline from spaceline

(require 'spaceline)
(require 'spaceline-config)

(spaceline-spacemacs-theme)

(spaceline-toggle-minor-modes-off)

(setq powerline-default-separator 'arrow-fade)

Benchmark

(rougier/report-time "Modeline")

Programming

(setq rougier/section-start-time (current-time))

General

Use yasnippets for generating useful snippets very useful in tandem with yasnippet-snippets package

(require 'yasnippet)
(yas-global-mode 1)                     ; Enable yasnippets globally
(setq yas-indent-line 'auto)            ; the other option is the value: 'fixed
(add-to-list 'auto-mode-alist '("[Mm]akefile\\'" . makefile-gmake-mode))

Workflow

Snakemake

(require 'snakemake-mode)
;; Enable snake mode
(add-to-list 'auto-mode-alist '("\\.smk\\'" . snakemake-mode))
(add-to-list 'auto-mode-alist '("[Ss]nakefile\\'" . snakemake-mode))

C/C++

Linux C standards

(defun linux-c-mode ()
"C mode with adjusted defaults for use with the Linux
kernel."
(interactive)
(c-mode)
(setq c-indent-level 8)
(setq c-brace-imaginary-offset 0)
(setq c-brace-offset -8)
(setq c-argdecl-indent 8)
(setq c-label-offset -8)
(setq c-continued-statement-offset 8)
(setq indent-tabs-mode nil)
(setq tab-width 8))
This will define the M-x linux-c-mode command. When hacking on a module, if
you put the string -*- linux-c -*- somewhere on the first two lines, this mode
will be automatically invoked. Also, you may want to add
(setq auto-mode-alist (cons '("/usr/src/linux.*/.*\\.[ch]
$" . linux-c-mode)
auto-mode-alist))

Lisp

Python

Configuration that helps writing python code a better experience.

List of requirements are:

  • Code completion
  • Documentation
  • Jump to definition
  • Send region/buffer to REPL (interpreted languages)
  • Identification of code issues
  • Auto format/organize/clean code
  • https://github.com/dadadel/pyment

Set python environment path

(setenv "PYTHONPATH" (shell-command-to-string "$SHELL --login -c 'echo -n $PYTHONPATH'"))

Setup elpy, in order to use all the features (such as navigation with M-.), you’ll need to install some python libraries:

pip install flake8 snakeviz
# For linux
sudo apt install python3-jedi black python3-autopep8 yapf3 python3-yapf
# For mac
brew install python3-jedi black python3-autopep8 yapf3 python3-yapf

Use snakeviz to profile code with `elpy-profile-buffer-or-region`

(require 'elpy)
(with-eval-after-load 'elpy
  (delete 'elpy-module-yasnippet elpy-modules))

(elpy-enable)

(setq elpy-rpc-python-command "python")
(setq elpy-remove-modeline-lighter nil)
(setq elpy-rpc-backend "jedi")          ; auto-completed backend
(setq python-check-command "flake8")    ; Syntax checker

(setq python-shell-interpreter "ipython"
      python-shell-interpreter-args "-i --simple-prompt"
      python-shell-prompt-detect-failure-warning nil)
(add-to-list 'python-shell-completion-native-disabled-interpreters
             "ipython")

;; remove highlight-indentation
(setq elpy-modules (delq 'elpy-module-highlight-indentation elpy-modules))

Setup flycheck with elpy

(require 'flycheck)

(when (require 'flycheck nil t)
  (setq elpy-modules (delq 'elpy-module-flymake elpy-modules))
  (add-hook 'elpy-mode-hook 'flycheck-mode))

Enforce column limits

;; COLUMN ENFORCEMENT
(require 'fill-column-indicator)
(add-hook 'python-mode-hook 'fci-mode)
(require 'column-enforce-mode)
(add-hook 'python-mode-hook 'column-enforce-mode)
(setq column-enforce-column 88)

Setup python virtual environments

(require 'pyenv-mode)
(pyenv-mode)

;; remove default keybindings
(unbind-key "C-c C-s" pyenv-mode-map)
(unbind-key "C-c C-u" pyenv-mode-map)

(setenv "WORKON_HOME" (concat (expand-file-name "~/.virtualenvs/versions/") (shell-command-to-string "echo $(pyenv version-name) | tr -d '\n'")))
(setenv "VIRTUALENVWRAPPER_HOOK_DIR" (concat (expand-file-name "~/.virtualenvs/versions/") (shell-command-to-string "echo $(pyenv version-name) | tr -d '\n'")))

Setup pyvenv

;; (setq pyvenv-exec-shell "/bin/zsh")
;; (setq pyvenv-virtualenvwrapper-python (shell-command-to-string "echo $(pyenv which python)"))

Setup sphinx-doc for sphinx compatible docstrings and documenting

(add-hook 'python-mode-hook (lambda ()
                              (require 'sphinx-doc)
                              (sphinx-doc-mode t)))
(setq sphinx-doc-include-types t)

Add python code folding hook with hideshow minor mode

(add-hook 'python-mode-hook 'hs-minor-mode)

Use isortify to sort python imports according to pep8 standards.

(require 'isortify)

Cython

Setup for cython mode


(require 'cython-mode)

(autoload 'cython-mode "cython-mode" "Mode for editing Cython source files")

(add-to-list 'auto-mode-alist '("\\.pyx\\'" . cython-mode))
(add-to-list 'auto-mode-alist '("\\.pxd\\'" . cython-mode))
(add-to-list 'auto-mode-alist '("\\.pxi\\'" . cython-mode))

Setup flycheck for cython mode


(require 'flycheck)

(add-hook 'cython-mode-hook 'flycheck-mode)

xml

Configure nxml mode

;; (add-hook 'nxml-mode-hook (lambda () (line-number-mode 0)))
;; (add-hook 'nxml-mode-hook (lambda () (linum-mode 0)))
(add-hook 'nxml-mode-hook (lambda () (undo-tree-mode -1)))

yaml

(require 'yaml-mode)

; If you wish to have Return key automatically indent cursor on new line
(add-hook 'yaml-mode-hook
          (lambda ()
            (define-key yaml-mode-map "\C-m" 'newline-and-indent))
          )

Latex

(require 'smartparens-latex)

(defun apm-latex-mode-setup ()
  "Tweaks and customisations for LaTeX mode."
  (turn-on-auto-fill)
  (abbrev-mode +1)
  ;; smartparens latex support
  (smartparens-mode +1)
  ;; Enable source-correlate for Control-click forward/reverse search.
  (TeX-source-correlate-mode 1)
  ;; enable math mode in latex
  (LaTeX-math-mode 1)
  ;; Enable reftex
  (turn-on-reftex)
  ;; integrate with company
  (company-auctex-init)
  )

(require 'tex-site)

(require 'company-auctex)
(company-auctex-init)

(setq-default TeX-auto-save t
              TeX-parse-self t
              TeX-PDF-mode t
              reftex-plug-into-AUCTeX t
              TeX-source-correlate-start-server t
              TeX-master nil
              )

(add-hook 'LaTeX-mode-hook #'apm-latex-mode-setup)

(add-hook 'LaTeX-mode-hook
          (lambda ()
            (font-latex-add-keywords '(("citep" "*[[{")) 'reference)
            (font-latex-add-keywords '(("citet" "*[[{")) 'reference)
            (font-latex-add-keywords '(("autoref" "*[[{")) 'reference))
          )

(setq TeX-command-extra-options "-shell-escape")

Add support for formatting latex text to one sentence per line with ospl mode. This helps tracking changes easier and better.

(add-hook 'LaTeX-mode-hook 'ospl-mode)
(add-hook 'LaTeX-mode-hook 'olivetti-mode)

nim

;; (require 'nim-mode)

Jinja

(message "Personal config :. jinja...")
;; Jinja
(use-package mmm-jinja2
  :straight t)
(use-package jinja2-mode
  :straight t)
(load "~/.emacs.d/elpa/mmm-jinja2-20170313.1420/mmm-jinja2.el")
(add-to-list 'auto-mode-alist '("\\.jinja2\\.wbt'" . html-mode))
(mmm-add-mode-ext-class 'html-mode "\\.jinja2\\.wbt'" 'jinja2)


(add-to-list 'auto-mode-alist '("\\.jinja2\\'" . yaml-mode))
(mmm-add-mode-ext-class 'yaml-mode "\\.jinja2\\'" 'jinja2)

Godot

Formatting code with gdformat requires installing gdtoolkit

pip install gdtoolkit
(setq gdscript-godot-executable "/path/to/godot")

GL

GLSL

(require 'glsl-mode)
(add-to-list 'auto-mode-alist '("\\.glsl\\'" . glsl-mode))

Benchmark


(rougier/report-time "Programming")

Completion


(setq rougier/section-start-time (current-time))

Company

Company is a text completion framework for Emacs. The name stands for “complete anything”. It uses pluggable back-ends and front-ends to retrieve and display completion candidates.

http://company-mode.github.io/

;; (add-hook 'after-init-hook 'global-company-mode)

(bind-key "C-c i" #'company-complete)

(add-hook 'emacs-lisp-mode-hook
          (lambda ()
            (paredit-mode t)
            (rainbow-delimiters-mode t)
            (show-paren-mode 1)
            ))

(add-hook 'lisp-interaction-mode
          (lambda ()
            (paredit-mode t)
            (rainbow-delimiters-mode t)
            (show-paren-mode 1)
            ))

Corfu

Corfu enhances completion at point with a small completion popup.


(require 'corfu)

(setq corfu-cycle t                ; Enable cycling for `corfu-next/previous'
      corfu-auto t                 ; Enable auto completion
      corfu-auto-delay 60.0        ; Delay before auto-completion shows up
      corfu-separator ?\s          ; Orderless field separator
      corfu-quit-at-boundary nil   ; Never quit at completion boundary
      corfu-quit-no-match t        ; Quit when no match
      corfu-preview-current nil    ; Disable current candidate preview
      corfu-preselect-first nil    ; Disable candidate preselection
      corfu-on-exact-match nil     ; Configure handling of exact matches
      corfu-echo-documentation nil ; Disable documentation in the echo area
      corfu-scroll-margin 5)       ; Use scroll margin

(global-corfu-mode)

A few more useful configurations…


;; TAB cycle if there are only few candidates
(setq completion-cycle-threshold 3)

;; Emacs 28: Hide commands in M-x which do not apply to the current mode.
;; Corfu commands are hidden, since they are not supposed to be used via M-x.
(setq read-extended-command-predicate
      #'command-completion-default-include-p)

;; Enable indentation+completion using the TAB key.
;; completion-at-point is often bound to M-TAB.
(setq tab-always-indent 'complete)

;; Completion in source blocks
(require 'cape)

(add-to-list 'completion-at-point-functions 'cape-symbol)

Orderless

Allow completion based on space-separated tokens, out of order.


(require 'orderless)

(setq completion-styles '(substring orderless basic)
      orderless-component-separator 'orderless-escapable-split-on-space
      read-file-name-completion-ignore-case t
      read-buffer-completion-ignore-case t
      completion-ignore-case t)

Benchmark


(rougier/report-time "Completion")

Writing

(setq rougier/section-start-time (current-time))

English

Tools and setup for making writing articles enjoyable and easy

Whisper

;; Speech to text
(require 'whisper)

(bind-key "C-c r" #'whisper-run)
(bind-key "C-c f" #'whisper-file)

(setq whisper-install-directory "~/softwares/install/speech2text/" ;; "/tmp/"
      whisper-model "base" ;; "medium" was tested but is too slow
      whisper-language "en"
      whisper-translate nil)

Langtool

(require 'langtool)

(bind-key "\C-x4w" #'langtool-check)
(bind-key "\C-x4W" #'langtool-check-done)
(bind-key "\C-x4l" #'langtool-switch-default-language)
(bind-key "\C-x44" #'langtool-show-message-at-point)
(bind-key "\C-x4c" #'langtool-correct-buffer)

(setq langtool-java-classpath "/opt/homebrew/Cellar/languagetool:/opt/homebrew/Cellar/languagetool/6.5/*")
(setq langtool-language-tool-jar "/opt/homebrew/Cellar/languagetool/6.5/libexec/languagetool-commandline.jar")
(setq langtool-language-tool-server-jar "/opt/homebrew/Cellar/languagetool/6.5/libexec/languagetool-server.jar")
(setq langtool-server-user-arguments '("-p" "8081" "--config" "~/.config/server.properties"))
(setq langtool-http-server-host "localhost")
(setq langtool-http-server-port 8081)

;; Text suggestions
(require 'flycheck-languagetool)

(setq flycheck-languagetool-server-jar "/opt/homebrew/Cellar/languagetool/6.5/libexec/languagetool-server.jar")
(setq flycheck-languagetool-server-port "8081")
(setq flycheck-languagetool-language "en-GB")

;; (add-hook 'text-mode-hook #'flycheck-languagetool-setup)
;; (add-hook 'LaTeX-mode-hook #'flycheck-languagetool-setup)
;; (add-hook 'org-mode-hook #'flycheck-languagetool-setup)
;; (add-hook 'org-mode-hook 'waveparticle/langtool-autosave-hook)
;; (add-hook 'text-mode-hook 'waveparticle/langtool-autosave-hook)
;; (add-hook 'LaTeX-mode-hook 'waveparticle/langtool-autosave-hook)

Proselint : Requires system installation first

`pip install proselint`

(require 'flycheck)

(flycheck-define-checker proselint
  "A linter for prose."
  :command ("proselint" source-inplace)
  :error-patterns
  ((warning line-start (file-name) ":" line ":" column ": "
            (id (one-or-more (not (any " ")))) (message) line-end))
  :modes (gfm-mode
          markdown-mode
          org-mode
          text-mode
          latex-mode))
  ;; :next-checkers ((warning . valec)))

Vale: Requires system installation first

`brew install vale`

(flycheck-define-checker valec
  "A checker for prose"
  :command ("vale" "--output" "line" source)
  :standard-input nil
  :error-patterns
  ((error line-start (file-name) ":" line ":" column ":"
          (id (one-or-more (not (any ":")))) ":" (message) line-end))
  :modes (markdown-mode
          org-mode
          text-mode
          latex-mode))
(add-to-list 'flycheck-checkers 'valec 'append)

Writegood-mode

(require 'writegood-mode)

(bind-key "C-c g" #'writegood-mode)
(bind-key "C-c C-g C-g" #'writegood-grade-level)
(bind-key "C-c C-g C-e" #'writegood-reading-ease)

(add-hook 'org-mode-hook #'writegood-mode)

Smog: Requires system installation first

`brew install style`

(require 'smog)

(setq smog-command "style -L en")

Powerthesaurus

Markdown

(add-hook 'markdown-mode-hook #'ospl-mode)

Benchmark

(rougier/report-time "Writing")

AI

GPTel

;; Use Ollama models instead of ChatGPT
;; OPTIONAL configuration
(setq
 gptel-model '"deepseek-coder:6.7b"
 gptel-backend (gptel-make-ollama "Ollama"
                 :host "localhost:11434"
                 :stream t
                 :models '("deepseek-coder:6.7b")))

RESOURCES

https://github.com/captainflasmr/ollama-buddy

A friendly Emacs interface for interacting with Ollama models. This package provides a convenient way to integrate Ollama’s local LLM capabilities directly into your Emacs workflow with little or no configuration required.

Org

(setq rougier/section-start-time (current-time))

General

(setq-default
 org-directory "~/projects/personal/org/core" ; Default org files
 org-ellipsis "  ·"                           ; Nicer ellipsis
 org-hide-emphasis-markers t                  ; Hide markers
 org-hide-leading-stars t                     ; Hide leading stars
 org-adapt-indentation nil
 org-cycle-separator-lines 0                  ; Number of empty lines between sections
 org-use-tag-inheritance nil                  ; Tags ARE NOT inherited
 org-use-property-inheritance t               ; Properties ARE inherited
 org-indent-indentation-per-level 2           ; Indentation per level
 org-indent-mode nil                          ; Org identation for better readability
 org-startup-indented t                       ; Startup indent
 org-startup-folded 'show2levels              ; Startup in folded view.
 org-link-use-indirect-buffer-for-internals t ; Indirect buffer for internal links
 org-fontify-quote-and-verse-blocks t         ; Specific face for quote and verse blocks
 org-src-fontify-natively t                   ; fontify src blocks
 org-src-tab-acts-natively t
 org-src-preserve-indentation nil             ; Preserve indentation for exporting
 org-fontify-whole-block-delimiter-line t     ; Fontify whole block
 org-edit-src-content-indentation 0
 org-return-follows-link nil                  ; Follow links when hitting return
 org-image-actual-width nil                   ; Resize image to window width
 org-indirect-buffer-display 'other-window    ; Tab on a task expand it in a new window
 org-outline-path-complete-in-steps nil       ; No steps in path display
 org-list-allow-alphabetical t                ; Allow alphabets in org list
 org-fold-catch-invisible-edits nil           ; Avoid editing invisible fields in org-mode
 org-list-demote-modify-bullet '(("+" . "-") ("-" . "+") ("*" . "+") ("1." . "a."))            ; Set the order of list symbols
 org-tags-column -80                          ; Set the tags alignment (88 following black!)
 org-auto-align-tags t
 org-catch-invisible-edits 'show-and-error    ; Disable invisible edits
 org-pretty-entities t                        ; insert special characters LaTeX-style
 org-confirm-babel-evaluate nil               ; No confirmation before executing code
 org-blank-before-new-entry '((heading) (plain-list-item . auto))
 org-archive-location ".%s_archive::"         ; Org archive files are hidden
 )

Better latex preview (see https://stackoverflow.com/questions/30151338)

(setq org-latex-create-formula-image-program 'dvisvgm)

We adapt fill functions according to the indent level.

(defun rougier/calc-offset-on-org-level ()
  "Calculate offset (in chars) on current level in org mode file."

  (* (or (org-current-level) 0) org-indent-indentation-per-level))

(defun rougier/org-fill-paragraph (&optional justify region)
  "Calculate apt fill-column value and fill paragraph."

  (let* ((fill-column (- fill-column (rougier/calc-offset-on-org-level))))
    (org-fill-paragraph justify region)))

(defun rougier/org-auto-fill-function ()
  "Calculate apt fill-column value and do auto-fill"

  (let* ((fill-column (- fill-column (rougier/calc-offset-on-org-level))))
    (org-auto-fill-function)))

(defun rougier/org-mode-hook ()
  (setq fill-paragraph-function #'rougier/org-fill-paragraph
        normal-auto-fill-function #'rougier/org-auto-fill-function))

(add-hook 'org-load-hook 'rougier/org-mode-hook)
(add-hook 'org-mode-hook 'rougier/org-mode-hook)

Org bullets to replace stars * with better symbols

(require 'org-bullets)
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1)))
;; Hide org markup for README
(setq org-hide-emphasis-markers t)

Add better looking icons for org tasks

(defun org-icons ()
  "Beautify org mode keywords."
  (setq prettify-symbols-alist '(("TODO" . "")
                                 ("WAIT" . "")
                                 ("NOPE" . "")
                                 ("DONE" . "")
                                 ("CONTACT" . "")
                                 ("[#A]" . "")
                                 ("[#B]" . "")
                                 ("[#C]" . "")
                                 ("[ ]" . "")
                                 ("[X]" . "")
                                 ("[-]" . "")
                                 ("#+BEGIN_SRC" . "")
                                 ("#+END_SRC" . "―")
                                 ("#+begin_src" . "")
                                 ("#+end_src" . "―")
                                 (":PROPERTIES:" . "")
                                 (":END:" . "―")
                                 ("#+STARTUP:" . "")
                                 ("#+TITLE: " . "")
                                 ("#+RESULTS:" . "")
                                 ("#+NAME:" . "")
                                 ("#+ROAM_TAGS:" . "")
                                 ("#+FILETAGS:" . "")
                                 ("#+HTML_HEAD:" . "")
                                 ("#+SUBTITLE:" . "")
                                 ("#+AUTHOR:" . "")
                                 (":Effort:" . "")
                                 ("SCHEDULED:" . "")
                                 ("DEADLINE:" . "")
                                 ))
  (prettify-symbols-mode))

(add-hook 'org-mode-hook 'org-icons)

org modern look

(with-eval-after-load 'org (global-org-modern-mode))

Fix for missing unicode symbol in org-modern

(setq org-modern-fold-stars
  '(("▶" . "▼")
    ("▷" . "▽")
    ("⏵" . "⏷")
    ("▹" . "▿")
    ("▸" . "▾")))
(setq org-modern-table nil)
(setq org-modern-table-vertical nil)
(setq org-modern-table-horizontal nil)
(add-hook 'org-mode-hook 'org-appear-mode)
(add-hook 'org-mode-hook 'olivetti-mode)
(add-hook 'org-mode-hook 'ospl-mode)

Key bindings

Org mode specific keybindings

(bind-key "C-c !" #'org-time-stamp-inactive)

Encryption

;; Enable org-crypt
(require 'org-crypt)
(org-crypt-use-before-save-magic)

;; Set default GPG key to use for encryption
;; Replace with your key ID from gpg --list-secret-keys
(setq org-crypt-key "shravantr@gmail.com")

;; Disable backup files for encrypted .org files
(setq backup-directory-alist
      `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms
      `((".*" ,temporary-file-directory t)))

;; Don't write sensitive data to disk in clear text
(setq auth-sources
    '((:source "~/.emacs.d/secrets/.authinfo.gpg")))

;; Encrypt all entries with the "crypt" tag
(setq org-tags-exclude-from-inheritance (quote ("crypt")))

;; Use minibuffer for GPG password prompt
(setq epg-pinentry-mode 'loopback)

;; Allow GPG to prompt for passphrase in Emacs
(setenv "GPG_AGENT_INFO" nil)

;; Force GPG to use minibuffer for password
(setq epg-gpg-program "gpg")  ; or "gpg" depending on your system

;; GPG key selection options
(setq epa-file-encrypt-to nil)
(setq epa-file-select-keys nil)

;; Auto-save settings for encrypted files
(setq org-crypt-disable-auto-save t)

;; Function to encrypt all marked regions in a file
(defun org-encrypt-all-entries ()
  "Encrypt all top-level entries in the current file."
  (interactive)
  (org-map-entries
   (lambda ()
     (org-encrypt-entry)
     (message "Encrypted entry %s" (org-get-heading t t t t)))
   "crypt" 'file))

;; Function to decrypt all entries temporarily
(defun org-decrypt-all-entries ()
  "Decrypt all entries in the current file."
  (interactive)
  (org-map-entries
   (lambda ()
     (org-decrypt-entry)
     (message "Decrypted entry %s" (org-get-heading t t t t)))
   "crypt" 'file))

echo GETPIN | pinentry-mac

Fixes

Have to downgrade to version 2.4.0 or below. Currently the easiest to do on MacOS is brew install gnupg@2.2.0

For documentation,

Resources

  • Useful commands

    • gpg-connect-agent reloadagent /bye

Templates

;; This is needed as of Org 9.2
(require 'org-tempo)

(add-to-list 'org-structure-template-alist '("sh" . "src sh"))
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("py" . "src python"))
(add-to-list 'org-structure-template-alist '("tex" . "src latex"))
(add-to-list 'org-structure-template-alist '("go" . "src go"))
(add-to-list 'org-structure-template-alist '("yaml" . "src yaml"))
(add-to-list 'org-structure-template-alist '("json" . "src json"))

Babel

;; Set languages org-babel supports
(org-babel-do-load-languages
 'org-babel-load-languages
 (quote
  ((awk .t)
   (ditaa .t)
   (shell .t)
   (python .t)
   (latex .t)
   (emacs-lisp .t)))
 )

Citations

Org 9.5 and above has native support for citations with `org-cite` package.

Links for reference:


(require 'bibtex)

(setq org-cite-export-processors
      '((md . (csl "chicago-fullnote-bibliography.csl"))   ; Footnote reliant
        (latex . biblatex)                                 ; For humanities
        (odt . (csl "chicago-fullnote-bibliography.csl"))  ; Footnote reliant
        (t . (csl "modern-language-association.csl"))      ; Fallback
        ))
(require 'org-ref)

Notes

Writing and reading notes with org

Exports

Setup default list of exporters


(setq org-export-backends '(ascii beamer html icalendar latex md odt))

Setup latex exports

;; Setup to run with minted packages to embed code in latex exports
(setq org-latex-pdf-process
      '("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
        "pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))

Export code with syntax highlighting using minted. Has a system dependency!

(setq org-latex-listings 'minted)
(setq org-latex-minted-options
      '(("frame" "lines") ("linenos=true")))

Blog

(require 'ox-hugo)

Getting Things Done (GTD)

Configure the refile file location to capture the notes

(setq org-directory "~/projects/personal/org/core/")
(setq org-agenda-files (list "refile.org"))

Setup a simple org capture template

(setq org-capture-templates
       `(("i" "Inbox" entry  (file "refile.org")
        ,(concat "* TODO %?\n" "/Entered on/ %U"))))

and setup a keyboard shortcut (C-c c):

(define-key global-map (kbd "C-c c") 'org-capture)

TASKS

(setq org-todo-keywords
      (quote ((sequence "TODO(t)" "WORKING(w)" "|" "DONE(d)" "DELEGATED(e@)")
              (sequence "HOLD(h@/!)" "FUTURE(f@/!)" "|" "CANCELLED(c@/!)" "PHONE(p@)" "MEETING(m@)")
              (sequence "BUY(b)" "|" "DROPPED(x@/!)" "BOUGHT(o)")
              (sequence "CONTACT(k)" "|" "DROPPED(x@/!)" "CONTACTED(j@/!)")
              )))
(setq org-todo-keyword-faces
      (quote (("TODO" :foreground "red" :weight bold)
              ("WORKING" :foreground "orange" :weight bold)
              ("DONE" :foreground "forest green" :weight bold)
              ("HOLD" :foreground "yellow" :weight bold)
              ("FUTURE" :foreground "magenta" :weight bold)
              ("CANCELLED" :foreground "forest green" :weight bold)
              ("MEETING" :foreground "forest green" :weight bold)
              ("PHONE" :foreground "forest green" :weight bold)
              ("CONTACT" :foreground "blue" :weight bold)
              )
             )
      )

RESOURCES

Benchmark

(rougier/report-time "Org")

Agenda

General

Load libraries.

(require 'org-agenda)
(require 'org-agenda-property)

Open agenda(s)

(bind-key "C-c a" #'org-agenda)

Files

(setq org-agenda-files
      '(
       "~/projects/personal/org/work/"
       "~/projects/personal/org/work/career/"
       "~/projects/personal/org/work/dev/"
       "~/projects/personal/org/work/research/"
       "~/projects/personal/org/personal/"
       "~/projects/personal/org/personal/daily/"
       "~/projects/personal/org/personal/lifestyle/"
       "~/projects/personal/org/projects/"
       "~/projects/personal/org/library/"
       )
      )

Settings

(setq org-agenda-window-setup 'current-window
      org-agenda-restore-windows-after-quit t
      org-agenda-show-all-dates nil
      org-agenda-time-in-grid t
      org-agenda-show-current-time-in-grid t
      org-agenda-start-on-weekday 1
      org-agenda-span 7
      org-agenda-hide-tags-regexp "." ; No tags
    ; org-agenda-hide-tags-regexp nil) ; All tags
      org-agenda-tags-column 0
    ; org-agenda-tags-column -79)      ; Left aling
      org-agenda-block-separator nil
      org-agenda-category-icon-alist nil
      org-agenda-skip-deadline-if-done t
      org-agenda-skip-scheduled-if-done t
      org-agenda-sticky t)

Prefix format

(setq org-agenda-prefix-format
      '((agenda . "%i %?-12t%s")
        (todo .   "%i")
        (tags .   "%i")
        (search . "%i")))

Sorting strategy

(setq org-agenda-sorting-strategy
      '((agenda deadline-down scheduled-down todo-state-up time-up
                habit-down priority-down category-keep)
        (todo   priority-down category-keep)
        (tags   timestamp-up priority-down category-keep)
        (search category-keep)))

Minimal time grid

(setq org-agenda-time-grid
      '((daily today require-timed)
        ()
        "......" "----------------"))

(setq org-agenda-current-time-string "   now")

Holidays

(require 'cal-iso)
(require 'holidays)

(defvar us-holidays nil
  "US holidays")

(defvar india-holidays nil
  "India holidays")

(defvar birthdays nil
  "Birthdays")

(setq us-holidays
      `((holiday-fixed 1 1 "New year's Day")
            (holiday-fixed 5 1 "Labour Day")
            (holiday-fixed 5 8 "Victory in Europe Day")
            (holiday-fixed 7 14 "Bastille day")
            (holiday-fixed 8 15 "Assumption of Mary")
            (holiday-fixed 11 11 "Armistice 1918")
            (holiday-fixed 11 1 "All Saints' Day")
            (holiday-fixed 12 25 "Christmas Day")
            (holiday-easter-etc 0 "Easter Sunday")
        (holiday-easter-etc 1 "Easter Monday")
        (holiday-easter-etc 39 "Ascension Day")
        (holiday-easter-etc 50 "Whit Monday")
        (holiday-sexp
         '(if (equal
               (holiday-easter-etc 49)
               (holiday-float 5 0 -1 nil))
              (car (car (holiday-float 6 0 1 nil)))
            (car (car (holiday-float 5 0 -1 nil))))
         "Mother's Day")))

(setq calendar-holidays french-holidays     ; French holidays
      calendar-week-start-day 1             ; Week starts on Monday
      calendar-mark-diary-entries-flag nil) ; Do not show diary entries

; Mark today in calendar
(add-hook 'calendar-today-visible-hook  #'calendar-mark-today)

Week day name with holidays

(defun rougier/org-agenda-format-date (date)
  "Org agenda date format displaying holidays"
  (let* ((dayname (calendar-day-name date))
         (day (cadr date))
         (month (car date))
         (monthname (calendar-month-name month))
         (year (nth 2 date))
         (holidays (calendar-check-holidays date)))
    (concat "\n"
            dayname " "
            (format "%d " day)
            monthname " "
            (format "%d" year)
            (if holidays (format " (%s)" (nth 0 holidays)))
            "\n")))

(setq org-agenda-format-date #'rougier/org-agenda-format-date)

Daily agenda

The daily agenda

(add-to-list 'org-agenda-custom-commands
             '("a" "Agenda"
               ((agenda "Agenda"
                        ((org-agenda-todo-keyword-format "%s")
                         (org-agenda-skip-deadline-if-done nil)
                         (org-deadline-warning-days 3)
                         (org-agenda-overriding-header nil))))))

Some decorations for the agenda

(defun rougier/org-agenda-highlight-todo (x)
  (let* ((done (string-match-p (regexp-quote ":DONE:") x))
         (canceled (string-match-p (regexp-quote "~") x))
         (x (replace-regexp-in-string ":TODO:" "" x))
         (x (replace-regexp-in-string ":DONE:" "" x))
         (x (replace-regexp-in-string "~" "" x))
         (x (if (and (boundp 'org-agenda-dim) org-agenda-dim)
                (propertize x 'face 'nano-faded) x))
         (x (if done (propertize x 'face 'nano-faded) x))
         (x (if canceled (propertize x 'face 'nano-faded) x)))
    x))

(advice-add 'org-agenda-highlight-todo
            :filter-return #'rougier/org-agenda-highlight-todo)

Timestamp tags for the agenda (bold means inverse video below):

now -> now 9:00 -> 9h00 9:30-10:00 -> 9h30 | 30mn -> ANYTIME

(require 'svg-lib)
(require 'svg-tag-mode)

(defun rougier/svg-tag-timestamp (&rest args)
  "Create a timestamp SVG tag for the time at point."

  (interactive)
  (let ((inhibit-read-only t))

    (goto-char (point-min))
    (while (search-forward-regexp
            "\\(\([0-9]/[0-9]\):\\)" nil t)
              (set-text-properties (match-beginning 1) (match-end 1)
                             `(display ,(svg-tag-make "ANYTIME"
                                                      :face 'nano-faded
                                                      :inverse nil
                                                      :padding 3 :alignment 0))))

    (goto-char (point-min))
    (while (search-forward-regexp
            "\\([0-9]+:[0-9]+\\)\\(\\.+\\)" nil t)

              (set-text-properties (match-beginning 1) (match-end 2)
                             `(display ,(svg-tag-make (match-string 1)
                                                       :face 'nano-faded
                                                       :margin 4 :alignment 0))))

    (goto-char (point-min))
    (while (search-forward-regexp
            "\\([0-9]+:[0-9]+\\)\\(\\.*\\)" nil t)

              (set-text-properties (match-beginning 1) (match-end 2)
                             `(display ,(svg-tag-make (match-string 1)
                                                      :face 'nano-default
                                                      :inverse t
                                                      :margin 4 :alignment 0))))
    (goto-char (point-min))
    (while (search-forward-regexp
            "\\([0-9]+:[0-9]+\\)\\(-[0-9]+:[0-9]+\\)" nil t)
      (let* ((t1 (parse-time-string (match-string 1)))
             (t2 (parse-time-string (substring (match-string 2) 1)))
             (t1 (+ (* (nth 2 t1) 60) (nth 1 t1)))
             (t2 (+ (* (nth 2 t2) 60) (nth 1 t2)))
             (d  (- t2 t1)))

        (set-text-properties (match-beginning 1) (match-end 1)
                                `(display ,(svg-tag-make (match-string 1)
                                                         :face 'nano-faded
                                                         :crop-right t)))
        ;; 15m: ¼, 30m:½, 45m:¾
        (if (< d 60)
             (set-text-properties (match-beginning 2) (match-end 2)
                                  `(display ,(svg-tag-make (format "%2dm" d)
                                                           :face 'nano-faded
                                                           :crop-left t :inverse t)))
           (set-text-properties (match-beginning 2) (match-end 2)
                                `(display ,(svg-tag-make (format "%1dH" (/ d 60))
                                                         :face 'nano-faded
                                                         :crop-left t :inverse t
                                                         :padding 2 :alignment 0))))))))
(add-hook 'org-agenda-mode-hook #'rougier/svg-tag-timestamp)
(advice-add 'org-agenda-redo :after #'rougier/svg-tag-timestamp)

Tasks agenda

A custom date format function using svg tags (progress pies) for the task agenda.

(defun rougier/org-agenda-custom-date ()
  (interactive)
  (let* ((timestamp (org-entry-get nil "TIMESTAMP"))
         (timestamp (or timestamp (org-entry-get nil "DEADLINE"))))
    (if timestamp
        (let* ((delta (- (org-time-string-to-absolute (org-read-date nil nil timestamp))
                         (org-time-string-to-absolute (org-read-date nil nil ""))))
               (delta (/ (+ 1 delta) 30.0))
               (face (cond ;; ((< delta 0.25) 'nano-popout)
                           ;; ((< delta 0.50) 'nano-salient)
                           ((< delta 1.00) 'nano-default)
                           (t 'nano-faded))))
          (concat
           (propertize " " 'face nil
                       'display (svg-lib-progress-pie
                                 delta nil
                                 :background (face-background face nil 'default)
                                 :foreground (face-foreground face)
                                 :margin 0 :stroke 2 :padding 1))
           " "
           (propertize
            (format-time-string "%d/%m" (org-time-string-to-time timestamp))
            'face 'nano-popout)))
      "     ")))

The task agenda

(add-to-list 'org-agenda-custom-commands
        '("x" "Tasks"
          ((todo "TODO" ;; "PROJECT"
                 ( (org-agenda-todo-keyword-format ":%s:")
                   (org-agenda-prefix-format '((todo   . " ")))
                   (org-agenda-skip-function '(org-agenda-skip-entry-if 'timestamp))
                   (org-agenda-overriding-header (propertize " Todo \n" 'face 'nano-strong))))

           (tags "+TALK+TIMESTAMP>=\"<now>\""
                 ((org-agenda-span 90)
                  (org-agenda-max-tags 5)
                  (org-agenda-prefix-format '((tags   . " %(rougier/org-agenda-custom-date) ")))
                  (org-agenda-overriding-header "\n Upcoming talks\n")))

           (tags "TEACHING+TIMESTAMP>=\"<now>\""
                 ((org-agenda-span 90)
                  (org-agenda-max-tags 5)
                  (org-agenda-prefix-format '((tags   . " %(rougier/org-agenda-custom-date) ")))
                  (org-agenda-overriding-header "\n Upcoming lessons\n")))

           (tags "TRAVEL+TIMESTAMP>=\"<now>\""
                 ((org-agenda-span 90)
                  (org-agenda-max-tags 5)
                  (org-agenda-prefix-format '((tags .  " %(rougier/org-agenda-custom-date) ")))
                  (org-agenda-overriding-header "\n Upcoming travels\n")))

           (tags "DEADLINE>=\"<today>\""
                  ((org-agenda-span 90)
                   (org-agenda-max-tags 5)
                   (org-agenda-prefix-format '((tags .  " %(rougier/org-agenda-custom-date) ")))
                   (org-agenda-overriding-header "\n Upcoming deadlines\n"))))))

Update

We install a time to refresh the daily agenda (a) at regular intervals such that the current time is up to date.

(defvar rougier/org-agenda-update-delay 60)
(defvar rougier/org-agenda-update-timer nil)

(defun rougier/org-agenda-update ()
  "Refresh daily agenda view"

  (when rougier/org-agenda-update-timer
    (cancel-timer rougier/org-agenda-update-timer))

  (let ((window (get-buffer-window "*Org Agenda(a)*" t)))
    (when window
      (with-selected-window window
        (let ((inhibit-message t))
          (org-agenda-redo)))))

  (setq rougier/org-agenda-update-timer
    (run-with-idle-timer
     (time-add (current-idle-time) rougier/org-agenda-update-delay)
     nil
     'rougier/org-agenda-update)))

(run-with-idle-timer rougier/org-agenda-update-delay t 'rougier/org-agenda-update)

Versioning

Git


(require 'git-gutter)

(global-git-gutter-mode +1)

Configure ediff


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

Magit

Set keybindings for magit

(require 'llama)
;; Taken from prelude
;; Magit creates some global keybindings by default
;; but it's a nice to complement them with this one
(global-set-key (kbd "C-c g") 'magit-file-dispatch)
(global-set-key (kbd "C-=") 'er/expand-region)

Prevent magit from writing in the header line.

(advice-add 'magit-set-header-line-format :override #'ignore)

Add fringe on the left side of magit windows such that we can highlight region using the fringe.

(add-hook 'magit-mode-setup-hook
          #'(lambda ()
              (interactive)
              (set-window-fringes nil (* 2 (window-font-width)) 0)))

System

(require 'openwith)
(openwith-mode t)
(setq openwith-associations '(("\\.mp4\\'" "vlc" (file))))