- About
- Emacs Initialization
- Start Up
- Saving Configuration
- Backups
- Management
- Minibuffer Completion
- Auto-Completion
- Spelling
- Programming Languages and Tools
- Advanced Configuration
- Browser
- Calculator
- Calendar
- Color Display in Text
- Dashboard
- Displays Available Keybindings in Popup
- Documentation
- EditorConfig
- History
- Hydra
- Icons
- Indentations
- IRC
- Linters
- Mails
- Money Management
- Package Menu
- Parentheses
- Paste
- Project Interactions
- Snippets
- Spaces Around the Operators
- Recent Files
- Term Definitions
- Try Packages
- Version Control System
- Whitespaces and Kill
- Org Mode
After using GNU Emacs for a while, I decided to create my own config to simplify my daily life by adding scripts and useful functions. For more informations about myself, you can visit my website.
To manage package configurations, I use the use-package package from John Wiegley, that I recommend.
This configuration is mainly based on the following user configurations:
- David Wilson (
daviwil
); - John Wiegley (
jwiegley
); - Mathieu Marques (
angrybacon
); - Sacha Chua (
sachac
).
Thanks to them for their incredible work!
This section contains the basic commands you need to know to properly initialize your GNU Emacs.
To install packages, it is useful to configure the package sources.
(setq package-archives '(("elpa" . "https://elpa.gnu.org/packages/")
("melpa" . "https://melpa.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")))
To manage the package configurations with use-package
, you must install it with the following code snippet.
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(use-package delight :ensure t)
(use-package use-package-ensure-system-package :ensure t)
This section contains the packages and configurations needed to get started with GNU Emacs.
I mainly use pass under GNU Linux as password manager. However, I prefer to
store IRC and email passwords directly in an authinfo.gpg
file for
simplicity. Afterward, this encrypted file is placed in Syncthing to be able to
easily use my configuration on other devices.
(use-package auth-source
:ensure nil
:custom
(auth-sources '("~/.config/gnupg/shared/authinfo.gpg"
"~/.authinfo.gpg"
"~/.authinfo"
"~/.netrc")))
For a better user experience of GNU Emacs, here are the default values that I use.
(setq-default
ad-redefinition-action 'accept ; Silence warnings for redefinition
cursor-in-non-selected-windows t ; Hide the cursor in inactive windows
display-time-default-load-average nil ; Don't display load average
fill-column 80 ; Set width for automatic line breaks
help-window-select t ; Focus new help windows when opened
indent-tabs-mode nil ; Prefer spaces over tabs
inhibit-startup-screen t ; Disable start-up screen
initial-scratch-message "" ; Empty the initial *scratch* buffer
kill-ring-max 128 ; Maximum length of kill ring
load-prefer-newer t ; Prefer the newest version of a file
mark-ring-max 128 ; Maximum length of mark ring
read-process-output-max (* 1024 1024) ; Increase the amount of data reads from the process
scroll-conservatively most-positive-fixnum ; Always scroll by one line
select-enable-clipboard t ; Merge system's and Emacs' clipboard
tab-width 4 ; Set width for tabs
use-package-always-ensure t ; Avoid the :ensure keyword for each package
user-full-name "Terencio Agozzino" ; Set the full name of the current user
user-mail-address "[email protected]" ; Set the email address of the current user
vc-follow-symlinks t ; Always follow the symlinks
view-read-only t) ; Always open read-only buffers in view-mode
(column-number-mode 1) ; Show the column number
(fset 'yes-or-no-p 'y-or-n-p) ; Replace yes/no prompts with y/n
(global-hl-line-mode) ; Hightlight current line
(set-default-coding-systems 'utf-8) ; Default to utf-8 encoding
(show-paren-mode 1) ; Show the parent
To keep the user’s home and the ~/.emacs.d
folder as clean as possible, I
follow the XDG base directory specification. Be careful that GNU Emacs will not
create the appropriate folders if they do not exist. Therefore, it is necessary
to create them yourself.
mkdir ~/.cache/emacs ~/.local/share/emacs/
NOTE: you can find out more by going to my dotfiles.
(defvar xdg-bin (getenv "XDG_BIN_HOME")
"The XDG bin base directory.")
(defvar xdg-cache (getenv "XDG_CACHE_HOME")
"The XDG cache base directory.")
(defvar xdg-config (getenv "XDG_CONFIG_HOME")
"The XDG config base directory.")
(defvar xdg-data (getenv "XDG_DATA_HOME")
"The XDG data base directory.")
(defvar xdg-lib (getenv "XDG_LIB_HOME")
"The XDG lib base directory.")
Some GNU Emacs packages implicitly use the request package to make HTTP requests. By default, the data for this package is stored in the GNU Emacs directory. Let’s change this value to respect the XDG base directory specifications.
(use-package request
:custom
(request-storage-directory (expand-file-name (format "%s/emacs/request/" xdg-data))))
Similarly, GNU Emacs has the built-in url-cookie
package to manage
cookies. Let’s also change this value to respect the XDG base directory
specifications.
(use-package url-cookie
:ensure nil
:custom
(url-cookie-file (expand-file-name (format "%s/emacs/url/cookies/" xdg-data))))
To avoid overloading the GNU Emacs customization init.el
file made with the UI,
I add the generated code in a separate file.
(use-package cus-edit
:ensure nil
:custom (custom-file (expand-file-name (format "%s/emacs/custom.el" xdg-data)))
:config
(when (file-exists-p custom-file)
(load custom-file t)))
Spending most of our time on GNU Emacs, it is important to use a font that will make our reading easier. Source Code Pro is one of the best monospaced font.
(set-face-attribute 'default nil :font "Source Code Pro")
(set-fontset-font t 'latin "Noto Sans")
Do not forget to install this font with your system manager and to check the font installation.
fc-list | grep "Source Code Pro"
The main benefit of using Language Server Protocol (LSP) to configure the management of your programming languages is that LSP servers are also used by other text editors, increasing contributions to these packages. What could be better than benefiting from the larger community, while keeping a quality text editor ;-)
We need a Language Server Protocol (LSP) client to use different LSP servers according to the programming languages that we would like to use. That’s where lsp-mode comes in!
(use-package lsp-mode
:commands (lsp lsp-deferred)
:hook ((prog-mode . lsp-deferred)
(lsp-mode . lsp-enable-which-key-integration))
:custom
(lsp-enable-folding nil)
(lsp-enable-links nil)
(lsp-enable-snippet nil)
(lsp-keymap-prefix "C-c ;")
(lsp-session-file (expand-file-name (format "%s/emacs/lsp-session-v1" xdg-data)))
(read-process-output-max (* 1024 1024)))
In addition to lsp-mode
, it is possible to use lsp-ui
to get additional
information (e.g., documentation) when hovering a variable or a function.
(use-package lsp-ui
:hook (lsp-mode . lsp-ui-mode))
When using lsp
, it is likely that you will encounter programming errors. To
navigate through these errors via the minibuffer, you can use a package for
that. If like me, you use consult
with your minibuffer completion, then
consult-lsp
is made for you.
(use-package consult-lsp
:commands (consult-lsp-diagnostics consult-lsp-symbols))
Finally, if like me you need a debugger, dap-mode uses the Debug Adapter Protocol wire protocol for communication between client and Debug Server. You won’t find a better debugger.
(use-package dap-mode
:after lsp-mode
:config
(dap-mode t)
(dap-ui-mode t))
Contrary to what some users might think, use-package
is not a package
manager. To download and use packages that are not available in package sources,
I use straight. The snippet below takes care of installing straight
.
;; Bootstrap straight
(defvar bootstrap-version)
(let ((bootstrap-file
(expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
(bootstrap-version 5))
(unless (file-exists-p bootstrap-file)
(with-current-buffer
(url-retrieve-synchronously
"https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
'silent 'inhibit-cookies)
(goto-char (point-max))
(eval-print-last-sexp)))
(load bootstrap-file nil 'nomessage))
;; Integrates `straight' directly into the `use-package' package through the
;; `:straight' expression.
(straight-use-package 'use-package)
I am a fan of tomorrow-night and combined with doom-modeline it’s happiness! To
get icons in the doom-modeline
, you will need to install icons.
(use-package doom-themes
:config
(load-theme 'doom-tomorrow-night t)
(doom-themes-org-config))
(use-package doom-modeline
:init (doom-modeline-mode)
:custom
(doom-modeline-icon (display-graphic-p))
(doom-modeline-mu4e t))
Since we do not do things by halves, it is also interesting to visually differentiate “real” buffers (e.g., buffers that contain our work) from “unreal” buffers (e.g., popups) by giving the latter a darker color. From then on, solar-mode is the ideal package.
(use-package solaire-mode
:defer 0.1
:custom (solaire-mode-remap-fringe t)
:config (solaire-global-mode))
If you have to use the mouse with GNU Emacs, you probably have the wrong text editor. Let’s remove all those menu items related to the mouse interface.
(when window-system
(menu-bar-mode -1)
(scroll-bar-mode -1)
(tool-bar-mode -1)
(tooltip-mode -1))
A good practice is to use an .org
file to modify your GNU Emacs configuration
with org-mode
and to load this configuration via an .el
file. This way you can
maintain an org-mode
configuration and still get a faster load.
Using the async package and the org-babel-tangle
command, the code below will
executes org-babel-tangle
asynchronously when config.org
is saved, to update the
config.el
file. From then on, you only need to add a add the my/config-tangle
function to the after-save
hook and specify the loading of the config.el
file
into the init.el
file.
(use-package async
:after org
:preface
(defvar config-file (expand-file-name "config.org" user-emacs-directory)
"The configuration file.")
(defvar config-last-change (nth 5 (file-attributes config-file))
"The last modification time of the configuration file.")
(defvar show-async-tangle-results nil
"Keep *emacs* async buffers around for later inspection.")
(defun my/config-tangle ()
"Tangle the org file asynchronously."
(when (my/config-updated)
(setq config-last-change
(nth 5 (file-attributes config-file)))
(my/async-babel-tangle config-file)))
(defun my/config-updated ()
"Check if the configuration file has been updated since the last time."
(time-less-p config-last-change
(nth 5 (file-attributes config-file))))
(defun my/async-babel-tangle (org-file)
"Tangle the org file asynchronously."
(let ((init-tangle-start-time (current-time))
(file (buffer-file-name))
(async-quiet-switch "-q"))
(async-start
`(lambda ()
(require 'org)
(org-babel-tangle-file ,org-file))
(unless show-async-tangle-results
`(lambda (result)
(if result
(message "[✓] %s successfully tangled (%.2fs)"
,org-file
(float-time (time-subtract (current-time)
',init-tangle-start-time)))
(message "[✗] %s as tangle failed." ,org-file))))))))
It is important to have file backups available with GNU Emacs. It is rare when I have to go back into backups and when I do, it is to go back to a previous backup of a buffer.
Finally, some saved files require a command to be run so that changes within the
file are taken into account. The my/cmd-after-saved-file
function below handles
the command execution according to a file at each save of this file.
(use-package files
:ensure nil
:preface
(defvar afilename-cmd
`((,(format "%s/X11/Xresources" xdg-config) . ,(format "xrdb -merge %s/X11/Xresources" xdg-config))
(,(format "%s/xbindkeysrc" (getenv "HOME")) . "xbindkeys -p"))
"File association list with their respective command.")
(defun my/cmd-after-saved-file ()
"Execute a command after saved a specific file."
(let* ((match (assoc (buffer-file-name) afilename-cmd)))
(when match
(shell-command (cdr match)))))
:hook (after-save . my/cmd-after-saved-file)
:init
;; Create the "~/.cache/emacs/auto-save" folder if it does not exist.
(let ((auto-save-folder (expand-file-name
(file-name-as-directory
(expand-file-name (format "%s/emacs/auto-save/" xdg-cache))))))
(unless (file-exists-p (locate-user-emacs-file auto-save-folder))
(make-directory (locate-user-emacs-file auto-save-folder))))
:custom
(auto-save-file-name-transforms
`((".*" ,(expand-file-name (format "%s/emacs/auto-save/" xdg-cache) t))))
(backup-directory-alist
`(("." . ,(expand-file-name (format "%s/emacs/backups/" xdg-data)))))
(delete-old-versions t)
(vc-make-backup-files t)
(version-control t))
Section dedicated to managing buffers, files, and windows on GNU Emacs to provide a more pleasant experience.
Buffers can quickly become a mess to manage. To manage them better, I use the
ibuffer
built-in package instead of buffer-menu
, to have a nicer visual
interface with a syntax color.
In addition, some buffers may contain useful temporary information that should
not be killed by accident. I make sure to set the buffers *scratch*
and
*Messages*
to read-only.
(use-package ibuffer
:ensure nil
:preface
(defvar protected-buffers '("*scratch*" "*Messages*")
"Buffer that cannot be killed.")
(defun my/protected-buffers ()
"Protect some buffers from being killed."
(dolist (buffer protected-buffers)
(with-current-buffer buffer
(emacs-lock-mode 'kill))))
:bind ("C-x C-b" . ibuffer)
:init (my/protected-buffers))
For buffer navigation, the next function is a more efficient way to go to the
beginning of a line with move-beginning-of-line
(C-a
) and back-to-indentation
(M-m
).
(use-package imenu
:ensure nil
:preface
(defun my/smarter-move-beginning-of-line (arg)
"Move point back to indentation of beginning of line.
Move point to the first non-whitespace character on this line.
If point is already there, move to the beginning of the line.
Effectively toggle between the first non-whitespace character and
the beginning of the line.
If ARG is not nil or 1, move forward ARG - 1 lines first. If
point reaches the beginning or end of the buffer, stop there."
(interactive "^p")
(setq arg (or arg 1))
;; Move lines first
(when (/= arg 1)
(let ((line-move-visual nil))
(forward-line (1- arg))))
(let ((orig-point (point)))
(back-to-indentation)
(when (= orig-point (point))
(move-beginning-of-line 1))))
:bind (("C-a" . my/smarter-move-beginning-of-line)
("C-r" . imenu)))
Finally, it is useful to be able to move a line or a region up and down without
having to kill-region
(C-w
) and yank (C-y)
. With the move-text-up
and
move-text-down
commands, the move-text package allows to moves the current line
or if marked, the current region’s, whole lines.
(use-package move-text
:bind (("M-p" . move-text-up)
("M-n" . move-text-down))
:config (move-text-default-bindings))
Sometimes you may want to discard your changes to a file and revert to the saved version of this file.
(use-package autorevert
:ensure nil
:delight auto-revert-mode
:bind ("C-x R" . revert-buffer)
:custom (auto-revert-verbose nil)
:config (global-auto-revert-mode))
To manage your files, dired
is already a good file manager. To fine-tune its
use, let’s change some default values.
(use-package dired
:ensure nil
:commands (dired dired-jump)
:bind (:map dired-mode-map
("h" . dired-up-directory)
("j" . dired-next-line)
("k" . dired-previous-line)
("l" . dired-single-buffer))
:delight "Dired"
:custom
(dired-auto-revert-buffer t)
(dired-dwim-target t)
(dired-hide-details-hide-symlink-targets nil)
(dired-listing-switches "-alh --group-directories-first")
(dired-ls-F-marks-symlinks nil)
(dired-recursive-copies 'always))
To avoid dired
to keep buffers, I use dired-single.
(use-package dired-single
:after dired
:bind (:map dired-mode-map
([remap dired-find-file] . dired-single-buffer)
([remap dired-up-directory] . dired-single-up-directory)
("M-DEL" . dired-prev-subdir)))
By default, dired
opens files in plain text. This behavior is sometimes
undesirable. Hopefully, dired-open can be used to informs dired
that certain
desired file extensions must be opened with external packages to GNU Emacs.
(use-package dired-open
:after (dired dired-jump)
:custom (dired-open-extensions '(("mp4" . "mpv"))))
To know the type of file at a glance, all-the-icons-dired integrates icons
directly into dired
.
(use-package all-the-icons-dired
:if (display-graphic-p)
:hook (dired-mode . all-the-icons-dired-mode))
It is sometimes convenient to hide dotfiles. With dired-hide-dotfiles this becomes possible.
(use-package dired-hide-dotfiles
:hook (dired-mode . dired-hide-dotfiles-mode)
:bind (:map dired-mode-map
("H" . dired-hide-dotfiles-mode)))
I like being able to <TAB>
on a folder and see its contents, without me getting
into it. dired-subtree allows this behavior.
(use-package dired-subtree
:after dired
:bind (:map dired-mode-map
("<tab>" . dired-subtree-toggle)))
Finally, to manage folders with a large number of files, it may be useful to filter with dired-narrow
(use-package dired-narrow
:ensure nil
:bind (("C-c C-n" . dired-narrow)
("C-c C-f" . dired-narrow-fuzzy)))
Most of the time, I want to split a window and put the focus on it to perform an action. By default GNU Emacs does not give the focus to this new window. I have no idea why this is not the default behavior, but we can easily set this behavior.
(use-package window
:ensure nil
:bind (("C-x 2" . vsplit-last-buffer)
("C-x 3" . hsplit-last-buffer)
;; Don't ask before killing a buffer.
([remap kill-buffer] . kill-this-buffer))
:preface
(defun hsplit-last-buffer ()
"Focus to the last created horizontal window."
(interactive)
(split-window-horizontally)
(other-window 1))
(defun vsplit-last-buffer ()
"Focus to the last created vertical window."
(interactive)
(split-window-vertically)
(other-window 1)))
To maximize concentration, I prefer to only center individual windows and keep a default behavior when multiple windows are present. centered-window deals with this behavior.
(use-package centered-window
:custom
(cwm-centered-window-width 130)
(cwm-frame-internal-border 0)
(cwm-incremental-padding t)
(cwm-incremental-padding-% 2)
(cwm-left-fringe-ratio 0)
(cwm-use-vertical-padding t)
:config (centered-window-mode))
The way I move between several windows in GNU Emacs is by indicating the number of the window I want to move to. Most people use ace-window, but I prefer switch-window which displays the window number while hiding its content. I find this behavior more convenient than moving from window to window to get to the one we are looking for.
(use-package switch-window
:bind (("C-x o" . switch-window)
("C-x w" . switch-window-then-swap-buffer)))
There are times when I would like to bring back a windows layout with their
content. With the winner-undo
and winner-redo
commands from the built-in winner
package, I can easily do that.
(use-package winner
:ensure nil
:config (winner-mode))
Having a good minibuffer completion is important on GNU Emacs since it is one of
the elements we will frequently interact with. In the beginning I used helm, but
I found it to be memory intensive for the few features I was using. From this
observation I switched to ivy for many years, which is a faster and a lighter
framework than helm
. However, ivy
is still a framework.
Since then, newer completion systems have emerged (e.g., vertico, selectrum,
and icomplete-vertical), designed to be optimized for a single task and nested
with other packages with the same vision. That’s why I now use vertico
instead
of ivy
.
NOTE: selectrum
is also a good alternative to ivy
, but is less minimal than
vertico
.
(use-package vertico
:straight (:files (:defaults "extensions/*"))
:init (vertico-mode)
:bind (:map vertico-map
("C-<backspace>" . vertico-directory-up))
:custom (vertico-cycle t)
:custom-face (vertico-current ((t (:background "#1d1f21")))))
To enable richer annotations (e.g., summary documentation of the functions and variables, as well as having the size and the last consultation of the files) for minibuffer completions, marginalia is awesome.
(use-package marginalia
:after vertico
:init (marginalia-mode)
:custom
(marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil)))
If like me you like to have icons associated with candidates, you can use all-the-icons-completion.
(use-package all-the-icons-completion
:after (marginalia all-the-icons)
:hook (marginalia-mode . all-the-icons-completion-marginalia-setup))
By default, vertico
sorts the candidates according to their history position,
then by length and finally by alphabetical. To improves searching across
completion (e.g., by filter expressions separated by spaces), you should
use orderless (or prescient).
(use-package orderless
:custom
(completion-category-defaults nil)
(completion-category-overrides '((file (styles . (partial-completion)))))
(completion-styles '(orderless)))
There is nothing like a collection of commands to have additional completions. consult provides this collection.
(use-package consult
:after projectile
:bind (;; Related to the control commands.
("<help> a" . consult-apropos)
("C-x b" . consult-buffer)
("C-x M-:" . consult-complex-command)
("C-c k" . consult-kmacro)
;; Related to the navigation.
("M-g a" . consult-org-agenda)
("M-g e" . consult-error)
("M-g g" . consult-goto-line)
("M-g h" . consult-org-heading)
("M-g i" . consult-imenu)
("M-g k" . consult-global-mark)
("M-g l" . consult-line)
("M-g m" . consult-mark)
("M-g o" . consult-outline)
("M-g I" . consult-project-imenu)
;; Related to the search and selection.
("M-s G" . consult-git-grep)
("M-s g" . consult-grep)
("M-s k" . consult-keep-lines)
("M-s l" . consult-locate)
("M-s m" . consult-multi-occur)
("M-s r" . consult-ripgrep)
("M-s u" . consult-focus-lines)
("M-s f" . consult-find))
:custom
(completion-in-region-function #'consult-completion-in-region)
(consult-narrow-key "<")
(consult-project-root-function #'projectile-project-root)
;; Provides consistent display for both `consult-register' and the register
;; preview when editing registers.
(register-preview-delay 0)
(register-preview-function #'consult-register-preview))
Finally, embark is great if like me you like to interact directly with your
files (e.g., for renaming, deleting and copying) through your completion system
without having to go through dired
.
(use-package embark
:bind ("C-." . embark-act))
Auto-completion with GNU Emacs is mainly combined with LSP mode. Therefore the development of any programming language is made easier with auto-completion, which involves a completion at point followed by the display of a small pop-in containing the candidates.
Nowadays there are two main possibilities: company-mode and corfu. Personally I
have tried to make an attempt with corfu
but I always prefer company-mode
as it
seems to be easier and smoother to configure.
(use-package company
:after lsp-mode
:hook (lsp-mode . company-mode)
:custom
(company-begin-commands '(self-insert-command))
(company-idle-delay 0.5)
(company-minimum-prefix-length 1)
(company-show-quick-access t)
(company-tooltip-align-annotations 't))
To get nice candidate icons differentiating a function from a variable at a glance, I use company-box.
(use-package company-box
:if (display-graphic-p)
:after company
:hook (company-mode . company-box-mode))
No one is immune to spelling mistakes. It is therefore crucial to check our
spelling when we write documents, reply to an email or perform any other writing
task. With abbrev
, flyspell
, ispell
, and LanguageTool, you will have the weapons
to reduce those nasty little spelling and grammar mistakes.
According to a list of misspelled words, the abbrev
built-in package
automatically corrects these words on the fly.
(use-package abbrev
:ensure nil
:delight
:hook (text-mode . abbrev-mode)
:custom (abbrev-file-name (expand-file-name (format "%s/emacs/abbrev_defs" xdg-data)))
:config
(if (file-exists-p abbrev-file-name)
(quietly-read-abbrev-file)))
I use the flyspell
built-in package to enable spell checking on-the-fly in GNU
Emacs. A useful flyspell
command is flyspell-auto-correct-word
(M-TAB
) which
automatically corrects a word according to the best suggestion.
NOTE: a call to`flyspell-buffer` could be extremely slow.
(use-package flyspell
:ensure nil
:delight
:hook ((text-mode . flyspell-mode)
(prog-mode . flyspell-prog-mode))
:custom
;; Add correction to abbreviation table.
(flyspell-abbrev-p t)
(flyspell-default-dictionary "en_US")
(flyspell-issue-message-flag nil)
(flyspell-issue-welcome-flag nil))
To correct spelling mistakes, the ispell
built-in package use a spell checker
package (e.g., hunspell or aspell). The aspell
spell checker package would make
it easier to spot errors in camelCase, which can be handy when
programming. However, I personally use hunspell
which only checks the spelling
of comments in the code and because it is more consistent on fly spells than
aspell
.
To use hunspell
you need to install it with your system package manager, as well
as install the desired language dictionaries (e.g., hunspell-en_US
and
hunspell-fr
).
Finally, you can check that you have installed the language dictionaries by
using the hunspell -D
command.
(use-package ispell
:preface
(defun my/switch-language ()
"Switch between the English and French for ispell, flyspell, and LanguageTool."
(interactive)
(let* ((current-dictionary ispell-current-dictionary)
(new-dictionary (if (string= current-dictionary "en_US") "fr_BE" "en_US")))
(ispell-change-dictionary new-dictionary)
(if (string= new-dictionary "fr_BE")
(progn
(setq lsp-ltex-language "fr"))
(progn
(setq lsp-ltex-language "en-US")))
(flyspell-buffer)
(message "[✓] Dictionary switched to %s" new-dictionary)))
:custom
(ispell-hunspell-dict-paths-alist
'(("en_US" "/usr/share/hunspell/en_US.aff")
("fr_BE" "/usr/share/hunspell/fr_BE.aff")))
;; Save words in the personal dictionary without asking.
(ispell-silently-savep t)
:config
(setenv "LANG" "en_US")
(cond ((executable-find "hunspell")
(setq ispell-program-name "hunspell")
(setq ispell-local-dictionary-alist '(("en_US"
"[[:alpha:]]"
"[^[:alpha:]]"
"['’-]"
t
("-d" "en_US" )
nil
utf-8)
("fr_BE"
"[[:alpha:]ÀÂÇÈÉÊËÎÏÔÙÛÜàâçèéêëîïôùûü]"
"[^[:alpha:]ÀÂÇÈÉÊËÎÏÔÙÛÜàâçèéêëîïôùûü]"
"['’-]"
t
("-d" "fr_BE")
nil
utf-8))))
((executable-find "aspell")
(setq ispell-program-name "aspell")
(setq ispell-extra-args '("--sug-mode=ultra"))))
;; Ignore file sections for spell checking.
(add-to-list 'ispell-skip-region-alist '("#\\+begin_align" . "#\\+end_align"))
(add-to-list 'ispell-skip-region-alist '("#\\+begin_align*" . "#\\+end_align*"))
(add-to-list 'ispell-skip-region-alist '("#\\+begin_equation" . "#\\+end_equation"))
(add-to-list 'ispell-skip-region-alist '("#\\+begin_equation*" . "#\\+end_equation*"))
(add-to-list 'ispell-skip-region-alist '("#\\+begin_example" . "#\\+end_example"))
(add-to-list 'ispell-skip-region-alist '("#\\+begin_labeling" . "#\\+end_labeling"))
(add-to-list 'ispell-skip-region-alist '("#\\+begin_src" . "#\\+end_src"))
(add-to-list 'ispell-skip-region-alist '("\\$" . "\\$"))
(add-to-list 'ispell-skip-region-alist '(org-property-drawer-re))
(add-to-list 'ispell-skip-region-alist '(":\\(PROPERTIES\\|LOGBOOK\\):" . ":END:")))
LanguageTool is great for correcting your grammar while you are writing or
saving your buffer. Combined with abbrev-mode
and flyspell
, you will have better
quality documents. To use LanguageTool with LSP mode, the lsp-ltex package is
what you need. The first time you use it, it will download the LTEX Language
Server LSP server for you.
NOTE: I don’t hook lsp-ltex
to text-mode
since it would process the config.org
file which has too many errors to be processed properly.
(use-package lsp-ltex
:disabled
:custom
(lsp-ltex-enabled nil)
(lsp-ltex-mother-tongue "fr"))
Section dedicated to the definition and customization of different programming languages and their tools. If you have the need to specify other programming languages, please check out the languages section of the LSP mode website.
Being a UNIX user, I often do Bash. Since GNU Emacs already supports it, I only
use the bash-language-server (bash-ls
) as LSP server. To use it, do not forget
to configure the LSP package and to install this LSP server through LSP mode or
with your system package manager.
Finally, the following snippet ensures that execution right (with chmod +x
) is
automatically granted to save a shell script file that begins with a #!
shebang.
(use-package sh-script
:ensure nil
:hook (after-save . executable-make-buffer-file-executable-if-script-p))
When I develop in C++ with GNU Emacs, I use the ccls LSP server. To use it, do not forget to configure the LSP package and to install this LSP server through your system package manager.
(use-package ccls
:after projectile
:hook ((c-mode c++-mode objc-mode cuda-mode) . lsp-deferred)
:custom
(ccls-args nil)
(ccls-executable (executable-find "ccls"))
(projectile-project-root-files-top-down-recurring
(append '("compile_commands.json" ".ccls")
projectile-project-root-files-top-down-recurring))
:config (add-to-list 'projectile-globally-ignored-directories ".ccls-cache"))
To allow ccls
to know the dependencies of your .cpp
files with your .h
files, it is important to provide an compile.commands.json
file (or a .ccls
file) at the root of your project.
For this, nothing could be easier. If like me you use a CMakeLists.txt
file for
all your C++ projects, then you just need to install the cmake
package on your
operating system. From then on, the compile.commands.json
file is generated with
the following commands:
cmake -H. -BDebug -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=YES
ln -s Debug/compile_commands.json
Finally, if you follow Google’s C/++ conventions like I do, the google-c-style package changes some default values to ensure that you follow these conventions as much as possible.
(use-package google-c-style
:hook (((c-mode c++-mode) . google-set-c-style)
(c-mode-common . google-make-newline-indent)))
CMake is a cross-platform build system generator. For its support with GNU Emacs, the cmake-mode package with the installation of the cmake-language-server LSP server is sufficient. To use it, do not forget to configure the LSP package and to install this LSP server through LSP mode or with your system package manager.
(use-package cmake-mode
:hook (cmake-mode . lsp-deferred)
:mode ("CMakeLists\\.txt\\'" "\\.cmake\\'"))
To better manage syntax colors, especially with respect to function arguments, it is visually useful to install cmake-font-lock.
(use-package cmake-font-lock
:hook (cmake-mode . cmake-font-lock-activate))
Finally, to compile with CMake in C++, I use cmake-ide, by indicating where the
CMakeLists.txt
file is present in the project.
(use-package cmake-ide
:after projectile
:init (cmake-ide-setup)
:hook (c++-mode . my/cmake-ide-find-project)
:preface
(defun my/cmake-ide-find-project ()
"Find the directory of the project for cmake-ide."
(with-eval-after-load 'projectile
(setq cmake-ide-project-dir (projectile-project-root))
(setq cmake-ide-build-dir (concat cmake-ide-project-dir "build")))
(setq cmake-ide-compile-command
(concat "cd " cmake-ide-build-dir " && cmake .. && make"))
(cmake-ide-load-db))
(defun my/switch-to-compilation-window ()
"Switch to the *compilation* buffer after compilation."
(other-window 1))
:bind ([remap comment-region] . cmake-ide-compile)
:config (advice-add 'cmake-ide-compile :after #'my/switch-to-compilation-window))
For my viewing pleasure, csv-mode
provides a color syntax when editing CSV
files.
(use-package csv-mode :mode ("\\.\\(csv\\|tsv\\)\\'"))
For the few times I have to develop in Dart, dart-mode with lsp-dart as server
LSP is great! To use it, do not forget to configure the LSP package and to
install dart
with your system package manager.
(use-package dart-mode
:after projectile
:mode "\\.dart\\'"
:config
(add-to-list 'projectile-project-root-files-bottom-up "pubspec.yaml")
(add-to-list 'projectile-project-root-files-bottom-up "BUILD"))
To configure lsp-dart
, nothing difficult. Remember to indicate the path to the
Dart SDK dir. Finally, if you use Flutter, do not forget to install it too with
your system package manager.
(use-package lsp-dart
:hook (dart-mode . lsp-deferred)
:custom
(lsp-dart-dap-flutter-hot-reload-on-save t)
(lsp-dart-sdk-dir "/opt/flutter/bin/cache/dart-sdk/"))
I often use Docker with Dockerfile. To support it with LSP and GNU Emacs, the
package dockerfile-mode with dockerfile-language-server-nodejs (dockerfile-ls
)
as LSP server is enough. To use it, do not forget to configure the LSP package
and to install this LSP server through LSP mode or with your system package
manager.
(use-package dockerfile-mode :delight "δ" :mode "Dockerfile\\'")
Sometimes I have to read digital books in EPUB format. The nov package allows to open this kind of file.
(use-package nov
:mode ("\\.epub\\'" . nov-mode)
:custom (nov-text-width 75))
Whether professionally or personally, it is often necessary to visualize your data in a quality graph. Gnuplot is the perfect tool for this and the gnuplot package allows to support this tool with GNU Emacs. To use gnuplot, do not forget to install it with your system package manager.
(use-package gnuplot
:mode "\\.\\(gp\\|gpi\\|plt\\)'"
:bind (:map gnuplot-mode-map
("C-c C-c". gnuplot-send-buffer-to-gnuplot)))
To modify the INI files, you need to install the ini-mode
package.
(use-package ini-mode :mode "\\.ini\\'")
To support Java with GNU Emacs, I use lsp-java as LSP client with Eclipse JDT
Language Server (jdtls
) as LSP server. To use it, do not forget to configure the
LSP package and to install this LSP server through LSP mode or with your system
package manager. If the LSP server is not yet installed, LSP will install it in
the ~/.emacs.d/.cache/lsp/eclipse.jdt.ls/
directory. Personally, I like to
follow XDG conventions by moving the LSP server to a more appropriate location
(e.g., ~/.local/lib/eclipse.jdt.ls
).
(use-package lsp-java
:hook (java-mode . lsp-deferred)
:custom (lsp-java-server-install-dir
(expand-file-name (format "%s/eclipse.jdt.ls/" xdg-lib))))
Most of my Java projects are made with gradle
. From then on, gradle-mode with
the configuration below allows me to compile my Java project with gradle easily.
(use-package gradle-mode
:hook (java-mode . gradle-mode)
:preface
(defun my/switch-to-compilation-window ()
"Switch to the *compilation* buffer after compilation."
(other-window 1))
:bind (:map gradle-mode-map
("C-c C-c" . gradle-build)
("C-c C-t" . gradle-test))
:config
(advice-add 'gradle-build :after #'my/switch-to-compilation-window)
(advice-add 'gradle-test :after #'my/switch-to-compilation-window))
JavaScript is one of those languages that needs a bit of setup time to get some
stability with GNU Emacs. By default GNU Emacs uses js-mode
as the major mode
for JavaScript buffers. However, I prefer to use js2-mode which is an enhanced
version of js-mode
. This package offers a better syntax highlighting and
proposes many other features.
As LSP server I use typescript-language-server (ts-ls
) which is the one
recommended by the LSP mode community. To use the LSP server, do not forget to
configure the LSP package and to install this LSP server through LSP mode or
with your system package manager
(use-package js2-mode
:ensure flycheck
:mode "\\.js\\'"
:hook ((js2-mode . js2-imenu-extras-mode)
(js2-mode . prettier-js-mode))
:custom (js-indent-level 2)
:config (flycheck-add-mode 'javascript-eslint 'js2-mode))
I like to use prettier to get my TypeScript code clean. To use it, do not forget to install it with your package manager.
(use-package prettier-js
:delight
:custom (prettier-js-args '("--print-width" "100"
"--single-quote" "true"
"--trailing-comma" "all")))
To get additional refactoring functions, I use the js2-refactor package. This
package also allows me to use the js2r-kill
commands which easily delete the
implementation of a function.
(use-package js2-refactor
:hook (js2-mode . js2-refactor-mode)
:bind (:map js2-mode-map
("C-k" . js2r-kill)
("M-." . lsp-find-definition)))
NOTE: I have long used xref-js2 to navigate through definitions and references in JavaScript. However, lsp-mode now already provides this functionality for us.
Finally, I sometimes take a look at the generated yarn.lock
file. To have a nice
syntax color and avoid modifying it, the yarn-mode package is perfect.
(use-package yarn-mode :mode "yarn\\.lock\\'")
JSON is probably the data format I use the most in the web. That’s why its setup below is a bit more advanced. As LSP server I prefer vscode-json-languageserver. To use it, make sure you install it with your package manager and to configure the LSP package.
(use-package json-mode
:delight "J"
:mode "\\.json\\'"
:hook (before-save . my/json-mode-before-save-hook)
:preface
(defun my/json-mode-before-save-hook ()
(when (eq major-mode 'json-mode)
(json-pretty-print-buffer)))
(defun my/json-array-of-numbers-on-one-line (encode array)
"Print the arrays of numbers in one line."
(let* ((json-encoding-pretty-print
(and json-encoding-pretty-print
(not (loop for x across array always (numberp x)))))
(json-encoding-separator (if json-encoding-pretty-print "," ", ")))
(funcall encode array)))
:config (advice-add 'json-encode-array :around #'my/json-array-of-numbers-on-one-line))
Being a lover of beautiful writing, it is important for me to have a stable
LaTeX environment. To have access to this stability, I use the tex-mode
built-in
package and texlab as LSP server. To use it, make sure you install it with your
package manager and to configure the LSP package.
With tex-mode
we need to ensure to install AUCTeX, which is a built-in package
for writing and formatting TeX files in GNU Emacs. With AUCTeX you can for
example use the =TeX-command-master
(C-c C-c
) command to compile your TeX files
and the LaTeX-environment
(C-c C-e
) command to insert a LaTeX environment.
(use-package tex
:ensure auctex
:preface
(defun my/switch-to-help-window (&optional ARG REPARSE)
"Switches to the *TeX Help* buffer after compilation."
(other-window 1))
:hook (LaTeX-mode . reftex-mode)
:bind (:map TeX-mode-map
("C-c C-o" . TeX-recenter-output-buffer)
("C-c C-l" . TeX-next-error)
("M-[" . outline-previous-heading)
("M-]" . outline-next-heading))
:custom
(TeX-auto-save t)
(TeX-byte-compile t)
(TeX-clean-confirm nil)
(TeX-master 'dwim)
(TeX-parse-self t)
(TeX-PDF-mode t)
(TeX-source-correlate-mode t)
(TeX-view-program-selection '((output-pdf "PDF Tools")))
:config
(advice-add 'TeX-next-error :after #'my/switch-to-help-window)
(advice-add 'TeX-recenter-output-buffer :after #'my/switch-to-help-window)
;; the ":hook" doesn't work for this one... don't ask me why.
(add-hook 'TeX-after-compilation-finished-functions 'TeX-revert-document-buffer))
Also, I like to use a TeX engine that can handle Unicode and use the font of my choice.
(setq-default TeX-engine 'xetex)
By default, LSP mode uses lsp-tex
as the LSP client for LaTeX. However, I prefer
to use lsp-latex which fully supports texlab
(cf. ROCKTAKEY/lsp-latex#26)
(use-package lsp-latex
:if (executable-find "texlab")
;; To properly load `lsp-latex', the `require' instruction is important.
:hook (LaTeX-mode . (lambda ()
(require 'lsp-latex)
(lsp-deferred)))
:custom (lsp-latex-build-on-save t))
To easier deal with \label
, \ref
, and \cite
commands in LaTeX, I use the reftex
built-in package.
(use-package reftex
:ensure nil
:custom
(reftex-save-parse-info t)
(reftex-use-multiple-selection-buffers t))
Finally, it is often useful to put our hands in a bibliography in LaTeX. The built-in
package bibtex
improves the visual and provides several commands.
(use-package bibtex
:ensure nil
:preface
(defun my/bibtex-fill-column ()
"Ensure that each entry does not exceed 120 characters."
(setq fill-column 120))
:hook ((bibtex-mode . lsp-deferred)
(bibtex-mode . my/bibtex-fill-column)))
I rarely program in Lua, but when I do, lua-mode with lua-language-server as LSP server satisfies me amply. To use it, do not forget to configure the LSP package and to install this LSP server through LSP mode or with your system package manager.
(use-package lua-mode :delight "Λ" :mode "\\.lua\\'")
To edit my files in Markdown, I use markdown-mode with unified-language-server as LSP server. To use it, do not forget to configure the LSP package and to install this LSP server with your system package manager. Added to that, to convert Markdown files, you can also install pandoc with your package manager system.
(use-package markdown-mode
:delight "μ"
:mode ("\\.\\(md\\|markdown\\)\\'")
:custom (markdown-command "/usr/bin/pandoc"))
Finally, it is always good to have a preview of the Markdown rendering. The markdown-preview-mode package allows this.
(use-package markdown-preview-mode
:commands markdown-preview-mode
:custom
(markdown-preview-javascript
(list (concat "https://github.com/highlightjs/highlight.js/"
"9.15.6/highlight.min.js")
"<script>
$(document).on('mdContentChange', function() {
$('pre code').each(function(i, block) {
hljs.highlightBlock(block);
});
});
</script>"))
(markdown-preview-stylesheets
(list (concat "https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/"
"3.0.1/github-markdown.min.css")
(concat "https://github.com/highlightjs/highlight.js/"
"9.15.6/styles/github.min.css")
"<style>
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 0 auto;
padding: 45px;
}
@media (max-width: 767px) { .markdown-body { padding: 15px; } }
</style>")))
I hope to never develop in PHP again. The few times I had to do it, I use mainly
use web-mode with intelephense (iph
) as LSP server for PHP and
vscode-html-languageserver (html-ls
) as LSP server for HTML. To use them, do not
forget to configure the LSP package and to install these LSP servers through LSP
mode or with your system package manager.
NOTE: to deal with HTML, I rarely use html-mode
which does not handle HTML well
in the presence of CSS and JavaScript.
(use-package web-mode
:delight "☸"
:preface
(defun enable-minor-mode (my-pair)
"Enable minor mode if filename match the regexp."
(if (buffer-file-name)
(if (string-match (car my-pair) buffer-file-name)
(funcall (cdr my-pair)))))
:mode ("\\.\\(html\\|jsx\\|php\\)\\'" . web-mode)
:hook (web-mode . (lambda ()
(enable-minor-mode
'("\\.jsx?\\'" . prettier-js-mode))))
:custom
(web-mode-attr-indent-offset 2)
(web-mode-block-padding 2)
(web-mode-css-indent-offset 2)
(web-mode-code-indent-offset 2)
(web-mode-comment-style 2)
(web-mode-enable-current-element-highlight t)
(web-mode-markup-indent-offset 2))
To make my UML diagrams with PlantUML, I use plantuml-mode. Make sure you
download the PlantUML compiled JAR and to indicate the path (e.g.,
~/.local/lib/plantuml.jar
) of this JAR file with the plantuml-jar-path
variable.
(use-package plantuml-mode
:mode ("\\.\\(plantuml\\|puml\\)\\'")
:custom (plantuml-jar-path
(expand-file-name (format "%s/plantuml.jar" xdg-lib))))
Python with GNU Emacs is one of the best supported languages. By using
python-mode
and pyright as LSP server, it’s fun to develop in Python. With
python-mode
I like to add some bindings to speed up the code code navigation in
Python. Besides that, I use autoflake to remove unused imports and variables.
(use-package python
:ensure flycheck
:delight "π"
:preface
(defun python-remove-unused-imports()
"Remove unused imports and unused variables with autoflake."
(interactive)
(if (executable-find "autoflake")
(progn
(shell-command (format "autoflake --remove-all-unused-imports -i %s"
(shell-quote-argument (buffer-file-name))))
(revert-buffer t t t))
(warn "[✗] python-mode: Cannot find autoflake executable.")))
:bind (:map python-mode-map
("M-[" . python-nav-backward-block)
("M-]" . python-nav-forward-block)
("M-|" . python-remove-unused-imports))
:custom
(flycheck-pylintrc "~/.pylintrc")
(flycheck-python-pylint-executable "/usr/bin/pylint"))
I have tried several LSP servers. I have experienced that mspyls
is faster than
pylsp
, but mspyls
has a memory leakage and became depreciated in favor of
pyright
. To configure pyright with GNU Emacs, the lsp-pyright package is
enough.
(use-package lsp-pyright
:if (executable-find "pyright")
;; To properly load `lsp-pyright', the `require' instruction is important.
:hook (python-mode . (lambda ()
(require 'lsp-pyright)
(lsp-deferred)))
:custom
(lsp-pyright-python-executable-cmd "python3")
(lsp-pyright-venv-path "~/.cache/pypoetry/virtualenvs/"))
To make sure my Python code is well formatted, I use black. Feel free to install it in your virtual environment or directly on your system.
(use-package blacken
:delight
:hook (python-mode . blacken-mode)
:custom (blacken-line-length 79))
To sort my Python imports, py-isort does a good job. Also, do not forget to install in your virtual environment or directly on your system.
(use-package py-isort
:hook ((before-save . py-isort-before-save)
(python-mode . pyvenv-mode)))
I use a single virtual environment for all my Python projects. The combination
of venv with pyvenv does the job well. When I encounter a Python buffer, my
virtual environment activates and stays activated even after I finish working in
Python. A better behavior would be to define a function through a
kill-buffer-hook
that would call the pyvenv-deactivate
command when all Python
buffers are closed.
(use-package pyvenv
:after python
:custom
(pyvenv-default-virtual-env-name (expand-file-name (format "%s/myenv/" xdg-data)))
(pyvenv-workon (expand-file-name (format "%s/myenv/" xdg-data)))
:config (pyvenv-tracking-mode))
Finally, to better manage the different versions of Python through projects, I
use pyenv through pyenv-mode-mode. To use it, make sure you have pyenv
installed
on your system.
(use-package pyenv-mode
:hook ((python-mode . pyenv-mode)
(projectile-switch-project . projectile-pyenv-mode-set))
:custom (pyenv-mode-set "3.8.5")
:preface
(defun projectile-pyenv-mode-set ()
"Set pyenv version matching project name."
(let ((project (projectile-project-name)))
(if (member project (pyenv-mode-versions))
(pyenv-mode-set project)
(pyenv-mode-unset)))))
For handling SQL files, the sql-mode
built-in package of GNU Emacs with the sqls
LSP server does the job.
(use-package sql-mode
:ensure nil
:mode "\\.sql\\'")
Finally, I use sql-indent to better manage the indentations of my SQL queries.
(use-package sql-indent
:delight sql-mode "Σ"
:hook (sql-mode . sqlind-minor-mode))
GNU Emacs already has built-in packages for style sheet languages (e.g., CSS,
LESS, and SCSS). Therefore, installing an appropriate LSP server and making sure
that the variables are properly indented is more than enough. For the LSP
server, I use vscode-css-languageserver (css-ls
) although it does not support
the LESS language. To use it, do not forget to configure the LSP package and to
install this LSP server through LSP mode or with your system package manager.
(use-package css-mode
:ensure flycheck
:mode "\\.css\\'"
:custom (css-indent-offset 2)
:config (flycheck-stylelintrc "~/.stylelintrc.json"))
For my TypeScript adventures, the typescript-mode package and the
typescript-language-server (ts-ls
) LSP server are more than enough. To use the
LSP server, do not forget to configure the LSP package and to install this LSP
server through LSP mode or with your system package manager if it is not already
done.
Finally, I also use prettier-js
to ensure proper indentation of my code. To
enable it, you must install the prettier
package with your package manager
system. Concerning the configuration of prettier, this is done in the JavaScript
section.
(use-package typescript-mode
:ensure flycheck
:hook ((typescript-mode . prettier-js-mode)
(typescript-mode . lsp-deferred))
:mode ("\\.\\(ts\\|tsx\\)\\'")
:custom
;; TSLint is depreciated in favor of ESLint.
(flycheck-disable-checker 'typescript-tslint)
(lsp-clients-typescript-server-args '("--stdio" "--tsserver-log-file" "/dev/stderr"))
(typescript-indent-level 2)
:config
(flycheck-add-mode 'javascript-eslint 'typescript-mode))
God keep me away from Vue.js. The few times I had to develop with it, I pulled
my hair out. For its support, I managed to get some semblance of stability with
the vue-mode package and the vue-language-server (vls
) LSP. However, the CSS
support with vue-mode
is not great. To use the LSP server, do not forget to
configure the LSP package and to install this LSP server through LSP mode or
with your system package manager.
(use-package vue-mode
:delight "V"
:hook (vue-mode . lsp-deferred)
:mode "\\.vue\\'"
:custom (vue-html-extra-indent 2))
To manage XML related files the nxml-mode
package built into GNU Emacs and the
LemMinX (xmlls
) LSP server does the job. To use it, do not forget to configure
the LSP package and to install this LSP server through LSP mode or with your
system package manager.
(use-package nxml-mode
:ensure nil
:hook (nxml-mode . lsp-deferred)
:mode ("\\.\\(xml\\|xsd\\|wsdl\\)\\'"))
When I have to develop through YAML files, the yaml-mode package with the yaml-language-server LSP server meets my needs. To use it, do not forget to configure the LSP package and to install this LSP server through LSP mode or with your system package manager.
(use-package yaml-mode
:delight "ψ"
:hook (yaml-mode . lsp-deferred)
:mode ("\\.\\(yaml\\|yml\\)\\'"))
This section contains the configuration of anecdotal GNU Emacs packages.
By default GNU Emacs provides the browse-url
package to open a browser based on
a query entered via our lovely text editor. Personally, I still prefer to
directly use my browser, namely qutebrowser. However, nothing prevents us from
configuring the browse-url
package.
(use-package browse-url
:ensure nil
:custom
(browse-url-browser-function 'browse-url-generic)
(browse-url-generic-program "qutebrowser"))
On a daily basis, you may have to make calculations or conversions from one unit
to another. With the calc
build-in package (C-x * c
) it is possible to do this easily
with GNU Emacs.
Among the useful commands within calc
that you should know:
calc-algebraic-entry
: allows you to enter a value and its unit to later to convert it or associate it with an operation.calc-convert-units
(u c
): converts the output of an algebraic entry (e.g.,25 m
→25000 mm
).calc-simplify-units
(u s
): simplifies the output of an algebraic entry (e.g.,5 m + 23 m
→5.023 m
).calc-view-units-table
(u V
): displays a table of units supported by calc.
(use-package calc
:ensure nil
:custom
(math-additional-units
'((GiB "1024 * MiB" "Giga Byte")
(MiB "1024 * KiB" "Mega Byte")
(KiB "1024 * B" "Kilo Byte")
(B nil "Byte")
(Gib "1024 * Mib" "Giga Bit")
(Mib "1024 * Kib" "Mega Bit")
(Kib "1024 * b" "Kilo Bit")
(b "B / 8" "Bit")))
;; Resets the calc's cache.
(math-units-table nil))
It is important to know the vacations and remember some official dates to better
organize your vacation planning and gift buying. The built-in calendar
and
holidays
packages are there to remind these dates to us.
(use-package calendar
:ensure nil
:bind ("C-c 0" . calendar)
:custom
(calendar-mark-holidays-flag t)
(calendar-week-start-day 1))
By default GNU Emacs fills in too many dates and most of the ones I am
interested in are not included. Especially those specific to vacations in my
country. For this reason, I specify to holidays
to hide some dates and add some
others.
(use-package holidays
:ensure nil
:custom
(holiday-bahai-holidays nil)
(holiday-hebrew-holidays nil)
(holiday-islamic-holidays nil)
(holiday-oriental-holidays nil)
(holiday-christian-holidays
'((holiday-fixed 1 6 "Epiphany")
(holiday-fixed 2 2 "Candlemas")
(holiday-easter-etc -47 "Mardi Gras")
(holiday-easter-etc 0 "Easter Day")
(holiday-easter-etc 1 "Easter Monday")
(holiday-easter-etc 39 "Ascension")
(holiday-easter-etc 49 "Pentecost")
(holiday-fixed 8 15 "Assumption")
(holiday-fixed 11 1 "All Saints' Day")
(holiday-fixed 11 2 "Day of the Dead")
(holiday-fixed 11 22 "Saint Cecilia's Day")
(holiday-fixed 12 1 "Saint Eloi's Day")
(holiday-fixed 12 4 "Saint Barbara")
(holiday-fixed 12 6 "Saint Nicholas Day")
(holiday-fixed 12 25 "Christmas Day")))
(holiday-general-holidays
'((holiday-fixed 1 1 "New Year's Day")
(holiday-fixed 2 14 "Valentine's Day")
(holiday-fixed 3 8 "International Women's Day")
(holiday-fixed 10 31 "Halloween")
(holiday-fixed 11 11 "Armistice of 1918")))
(holiday-local-holidays
'((holiday-fixed 5 1 "Labor Day")
(holiday-float 3 0 0 "Grandmothers' Day")
(holiday-float 4 4 3 "Secretary's Day")
(holiday-float 5 0 2 "Mother's Day")
(holiday-float 6 0 2 "Father's Day")
(holiday-fixed 7 21 "Belgian National Day"))))
To automatically display a color when typing a color or hex code values, the rainbow-mode package is useful.
(use-package rainbow-mode
:delight
:hook ((prog-mode text-mode) . rainbow-mode))
Organization is even more important in the 21st century than it was before. What
could be better than launching GNU Emacs with a dashboard that lists the tasks
of the week with org-agenda
and a list of projects we have recently contributed
to with projectile
. To our delight the dashboard package offers these features
and more.
(use-package dashboard
:custom
(dashboard-banner-logo-title "With Great Power Comes Great Responsibility!")
(dashboard-center-content t)
(dashboard-items '((agenda)
(projects . 5)))
(dashboard-projects-switch-function 'counsel-projectile-switch-project-by-name)
(dashboard-set-file-icons t)
(dashboard-set-footer nil)
(dashboard-set-heading-icons t)
(dashboard-set-navigator t)
(dashboard-startup-banner 'logo)
:config (dashboard-setup-startup-hook))
It is difficult to remember every keyboard shortcuts. The which-key package
helps to solve this. I used guide-key in my past days, but which-key
is a good
replacement.
(use-package which-key
:defer 0.2
:delight
:custom (which-key-idle-delay 0.5)
:config (which-key-mode))
To have a more user-friendly documentation I use the helpful package.
(use-package helpful
:commands (helpful-at-point
helpful-callable
helpful-command
helpful-function
helpful-key
helpful-macro
helpful-variable)
:bind
([remap display-local-help] . helpful-at-point)
([remap describe-function] . helpful-callable)
([remap describe-variable] . helpful-variable)
([remap describe-symbol] . helpful-symbol)
([remap describe-key] . helpful-key)
([remap describe-command] . helpful-command))
It often happens that you have to work on the same project as other developers. To keep a coding style you can use EditorConfig through the editorconfig package.
(use-package editorconfig
:defer 0.3
:config (editorconfig-mode))
There are times when it is necessary to remember a command. The savehist
built-in package allows you to save commands in a file so that you can run them
again later.
(use-package savehist
:ensure nil
:custom
(history-delete-duplicates t)
(history-length 25)
(savehist-file (expand-file-name (format "%s/emacs/history" xdg-cache)))
:config (savehist-mode))
GNU Emacs has so many commands per mode that it is tedious to remember all the keybindings for quick access. Fortunately, hydra allows you to create menu commands and on the basis of a popup, display the commands you have associated with it.
(use-package hydra
:bind (("C-c I" . hydra-image/body)
("C-c L" . hydra-ledger/body)
("C-c M" . hydra-merge/body)
("C-c T" . hydra-tool/body)
("C-c b" . hydra-btoggle/body)
("C-c c" . hydra-clock/body)
("C-c e" . hydra-circe/body)
("C-c f" . hydra-flycheck/body)
("C-c g" . hydra-go-to-file/body)
("C-c m" . hydra-magit/body)
("C-c o" . hydra-org/body)
("C-c p" . hydra-projectile/body)
("C-c s" . hydra-spelling/body)
("C-c t" . hydra-tex/body)
("C-c u" . hydra-upload/body)
("C-c w" . hydra-windows/body)))
(use-package major-mode-hydra
:after hydra
:preface
(defun with-alltheicon (icon str &optional height v-adjust face)
"Display an icon from all-the-icon."
(s-concat (all-the-icons-alltheicon icon :v-adjust (or v-adjust 0) :height (or height 1) :face face) " " str))
(defun with-faicon (icon str &optional height v-adjust face)
"Display an icon from Font Awesome icon."
(s-concat (all-the-icons-faicon icon ':v-adjust (or v-adjust 0) :height (or height 1) :face face) " " str))
(defun with-fileicon (icon str &optional height v-adjust face)
"Display an icon from the Atom File Icons package."
(s-concat (all-the-icons-fileicon icon :v-adjust (or v-adjust 0) :height (or height 1) :face face) " " str))
(defun with-octicon (icon str &optional height v-adjust face)
"Display an icon from the GitHub Octicons."
(s-concat (all-the-icons-octicon icon :v-adjust (or v-adjust 0) :height (or height 1) :face face) " " str)))
Group a lot of commands.
(pretty-hydra-define hydra-btoggle
(:hint nil :color amaranth :quit-key "q" :title (with-faicon "toggle-on" "Toggle" 1 -0.05))
("Basic"
(("a" abbrev-mode "abbrev" :toggle t)
("h" global-hungry-delete-mode "hungry delete" :toggle t))
"Coding"
(("e" electric-operator-mode "electric operator" :toggle t)
("F" flyspell-mode "flyspell" :toggle t)
("f" flycheck-mode "flycheck" :toggle t)
("l" lsp-mode "lsp" :toggle t)
("s" smartparens-mode "smartparens" :toggle t))
"UI"
(("i" ivy-rich-mode "ivy-rich" :toggle t))))
Group circe commands.
(pretty-hydra-define hydra-circe
(:hint nil :color teal :quit-key "q" :title (with-faicon "comments-o" "Circe" 1 -0.05))
("Action"
(("c" circe "connect")
("r" circe-reconnect "reconnect")
("u" my/circe-count-nicks "user"))))
Group clock commands.
(pretty-hydra-define hydra-clock
(:hint nil :color teal :quit-key "q" :title (with-faicon "clock-o" "Clock" 1 -0.05))
("Action"
(("c" org-clock-cancel "cancel")
("d" org-clock-display "display")
("e" org-clock-modify-effort-estimate "effort")
("i" org-clock-in "in")
("j" org-clock-goto "jump")
("o" org-clock-out "out")
("p" org-pomodoro "pomodoro")
("r" org-clock-report "report"))))
Group Flycheck commands.
(pretty-hydra-define hydra-flycheck
(:hint nil :color teal :quit-key "q" :title (with-faicon "plane" "Flycheck" 1 -0.05))
("Checker"
(("?" flycheck-describe-checker "describe")
("d" flycheck-disable-checker "disable")
("m" flycheck-mode "mode")
("s" flycheck-select-checker "select"))
"Errors"
(("<" flycheck-previous-error "previous" :color pink)
(">" flycheck-next-error "next" :color pink)
("f" flycheck-buffer "check")
("l" flycheck-list-errors "list"))
"Other"
(("M" flycheck-manual "manual")
("v" flycheck-verify-setup "verify setup"))))
Group jump-to-files commands.
(pretty-hydra-define hydra-go-to-file
(:hint nil :color teal :quit-key "q" :title (with-octicon "file-symlink-file" "Go To" 1 -0.05))
("Agenda"
(("ac" (find-file "~/.personal/agenda/contacts.org") "contacts")
("ah" (find-file "~/.personal/agenda/home.org") "home")
("ai" (find-file "~/.personal/agenda/inbox.org") "inbox")
("ap" (find-file "~/.personal/agenda/people.org") "people")
("ar" (find-file "~/.personal/agenda/routine.org") "routine")
("aw" (find-file "~/.personal/agenda/work.org") "work"))
"Config"
(("ca" (find-file (format "%s/alacritty/alacritty.yml" xdg-config)) "alacritty")
("cA" (find-file (format "%s/sh/aliases" xdg-config)) "aliases")
("ce" (find-file "~/.emacs.d/config.org") "emacs")
("cE" (find-file (format "%s/sh/environ" xdg-config)) "environ")
("cn" (find-file (format "%s/neofetch/config.conf" xdg-config)) "neofetch")
("cq" (find-file (format "%s/qutebrowser/config.py" xdg-config)) "qutebrowser")
("cr" (find-file (format "%s/ranger/rc.conf" xdg-config)) "ranger")
("cs" (find-file (format "%s/sway/config" xdg-config)) "sway")
("ct" (find-file (format "%s/tmux/tmux.conf" xdg-config)) "tmux")
("cw" (find-file (format "%s/waybar/config" xdg-config)) "waybar")
("cW" (find-file (format "%s/wofi/config" xdg-config)) "wofi")
("cx" (find-file (format "%s/sh/xdg" xdg-config)) "xdg"))
"Notes"
(("na" (find-file (format "~/.personal/notes/affirmations.pdf" xdg-config)) "Affirmations"))
"Other"
(("ob" (find-file "~/.personal/other/books.org") "book")
("ol" (find-file "~/.personal/other/long-goals.org") "long-terms goals")
("om" (find-file "~/.personal/other/movies.org"))
("op" (find-file "~/.personal/other/purchases.org") "purchase")
("os" (find-file "~/.personal/other/short-goals.org") "short-terms goals")
("ou" (find-file "~/.personal/other/usb.org") "usb")
("oL" (find-file "~/.personal/other/learning.org") "learning"))))
Group images commands.
(pretty-hydra-define hydra-image
(:hint nil :color pink :quit-key "q" :title (with-faicon "file-image-o" "Images" 1 -0.05))
("Action"
(("r" image-rotate "rotate")
("s" image-save "save" :color teal))
"Zoom"
(("-" image-decrease-size "out")
("+" image-increase-size "in")
("=" image-transform-reset "reset"))))
Group Ledger commands.
(pretty-hydra-define hydra-ledger
(:hint nil :color teal :quit-key "q" :title (with-faicon "usd" "Ledger" 1 -0.05))
("Action"
(("b" leadger-add-transaction "add")
("c" ledger-mode-clean-buffer "clear")
("i" ledger-copy-transaction-at-point "copy")
("s" ledger-delete-current-transaction "delete")
("r" ledger-report "report"))))
Group Magit commands.
(pretty-hydra-define hydra-magit
(:hint nil :color teal :quit-key "q" :title (with-octicon "mark-github" "Magit" 1 -0.05))
("Action"
(("b" magit-blame "blame")
("c" magit-clone "clone")
("i" magit-init "init")
("l" magit-log-buffer-file "commit log (current file)")
("L" magit-log-current "commit log (project)")
("s" magit-status "status"))))
Group Merge commands.
(pretty-hydra-define hydra-merge
(:hint nil :color pink :quit-key "q" :title (with-octicon "mark-github" "Magit" 1 -0.05))
("Move"
(("n" smerge-next "next")
("p" smerge-prev "previous"))
"Keep"
(("RET" smerge-keep-current "current")
("a" smerge-keep-all "all")
("b" smerge-keep-base "base")
("l" smerge-keep-lower "lower")
("u" smerge-keep-upper "upper"))
"Diff"
(("<" smerge-diff-base-upper "upper/base")
("=" smerge-diff-upper-lower "upper/lower")
(">" smerge-diff-base-lower "base/lower")
("R" smerge-refine "redefine")
("E" smerge-ediff "ediff"))
"Other"
(("C" smerge-combine-with-next "combine")
("r" smerge-resolve "resolve")
("k" smerge-kill-current "kill current"))))
Group Org commands.
(pretty-hydra-define hydra-org
(:hint nil :color teal :quit-key "q" :title (with-fileicon "org" "Org" 1 -0.05))
("Action"
(("A" my/org-archive-done-tasks "archive")
("a" org-agenda "agenda")
("c" org-capture "capture")
("d" org-decrypt-entry "decrypt")
("i" org-insert-link-global "insert-link")
("j" my/org-jump "jump-task")
("k" org-cut-subtree "cut-subtree")
("o" org-open-at-point-global "open-link")
("r" org-refile "refile")
("s" org-store-link "store-link")
("t" org-show-todo-tree "todo-tree"))))
Group Projectile commands.
(pretty-hydra-define hydra-projectile
(:hint nil :color teal :quit-key "q" :title (with-faicon "rocket" "Projectile" 1 -0.05))
("Buffers"
(("b" projectile-switch-to-buffer "list")
("k" projectile-kill-buffers "kill all")
("S" projectile-save-project-buffers "save all"))
"Find"
(("d" projectile-find-dir "directory")
("D" projectile-dired "root")
("f" projectile-find-file "file")
("p" consult-projectile "project"))
"Other"
(("i" projectile-invalidate-cache "reset cache"))
"Search"
(("r" projectile-replace "replace")
("R" projectile-replace-regexp "regexp replace")
("s" counsel-ripgrep "search"))))
Group org-roam
commands.
(pretty-hydra-define hydra-notes
(:hint nil :color teal :quit-key "q" :title (with-octicon "pencil" "Notes" 1 -0.05))
("Notes"
(("c" org-roam-dailies-capture-today "capture")
("C" org-roam-dailies-capture-tomorrow "capture tomorrow")
("g" org-roam-graph "graph")
("f" org-roam-node-find "find")
("i" org-roam-node-insert "insert"))
"Go To"
((">" org-roam-dailies-goto-next-note "next note")
("<" org-roam-dailies-goto-previous-note "previous note")
("d" org-roam-dailies-goto-date "date")
("t" org-roam-dailies-goto-today "today")
("T" org-roam-dailies-goto-tomorrow "tomorrow")
("y" org-roam-dailies-goto-yesterday "yesterday"))))
Group spelling commands.
(pretty-hydra-define hydra-spelling
(:hint nil :color teal :quit-key "q" :title (with-faicon "magic" "Spelling" 1 -0.05))
("Checker"
(("c" langtool-correct-buffer "correction")
("C" langtool-check-done "clear")
("d" ispell-change-dictionary "dictionary")
("l" (message "Current language: %s (%s)" langtool-default-language ispell-current-dictionary) "language")
("s" my/switch-language "switch")
("w" wiki-summary "wiki"))
"Errors"
(("<" flyspell-correct-previous "previous" :color pink)
(">" flyspell-correct-next "next" :color pink)
("f" langtool-check "find"))))
Group TeX commands.
(pretty-hydra-define hydra-tex
(:hint nil :color teal :quit-key "q" :title (with-fileicon "tex" "LaTeX" 1 -0.05))
("Action"
(("g" reftex-goto-label "goto")
("r" reftex-query-replace-document "replace")
("s" counsel-rg "search")
("t" reftex-toc "table of content"))))
Group Tool commands.
(pretty-hydra-define hydra-tool
(:hint nil :color teal :quit-key "q" :title (with-faicon "briefcase" "Tool" 1 -0.05))
("Network"
(("c" ipcalc "subnet calculator")
("i" ipinfo "ip info"))))
Group TypeScript commands.
(defhydra hydra-typescript (:color blue)
"
^
^TypeScript^ ^Do^
^──────────^──────────^──^───────────
_q_ quit _b_ back
^^ _e_ errors
^^ _j_ jump
^^ _r_ references
^^ _R_ restart
^^ ^^
"
("q" nil)
("b" tide-jump-back)
("e" tide-project-errors)
("j" tide-jump-to-definition)
("r" tide-references)
("R" tide-restart-server))
Group upload commands.
(pretty-hydra-define hydra-upload
(:hint nil :color teal :quit-key "q" :title (with-faicon "cloud-upload" "Upload" 1 -0.05))
("Action"
(("b" webpaste-paste-buffer "buffer")
("i" imgbb-upload "image")
("r" webpaste-paste-region "region"))))
Group window-related commands.
(pretty-hydra-define hydra-windows
(:hint nil :forein-keys warn :quit-key "q" :title (with-faicon "windows" "Windows" 1 -0.05))
("Window"
(("b" balance-windows "balance")
("i" enlarge-window "heighten")
("j" shrink-window-horizontally "narrow")
("k" shrink-window "lower")
("u" winner-undo "undo")
("r" winner-redo "redo")
("l" enlarge-window-horizontally "widen")
("s" switch-window-then-swap-buffer "swap" :color teal))
"Zoom"
(("-" text-scale-decrease "out")
("+" text-scale-increase "in")
("=" (text-scale-increase 0) "reset"))))
To integrate icons with doom-modeline
and other packages, all-the-icons is the
best package that you can have with GNU Emacs. To download the icons on your
machine, you must run the all-the-icons-install-fonts
command when you install
this package.
(use-package all-the-icons
:if (display-graphic-p)
:commands all-the-icons-install-fonts
:config (unless (find-font (font-spec :name "all-the-icons"))
(all-the-icons-install-fonts t)))
By default GNU Emacs auto-indents the code while typing with
electric-indent-mode
, but the indentation made is wrong when moving blocks,
transposing lines and so on. The aggresive-indent package is an improved version
of the previous mode, allowing a code to be always indented.
(use-package aggressive-indent
:custom (aggressive-indent-comments-too t))
With code nesting, it is important to always have a discrete visual on the indentation of our code. The highlight-indent-guides package allows you to see at a glance if an indentation is bad through block highlight.
(use-package highlight-indent-guides
:hook (prog-mode . highlight-indent-guides-mode)
:custom (highlight-indent-guides-method 'character))
IRC is the best way for me to get a quick answer to a question and to learn from more competent people than me on a subject.
To start using IRC, you will need to create an account. Once your account is
created, you can store it in your .authinfo.gpg
file and add this file to the
auth-sources
variable. To add your IRC account in the authinfo.gpg
file, add in
the following line with your credentials:
machine irc.libera.chat login <nickname> password <password> port 6697
After spending many years on ERC, I decided to move to circe as a IRC client which I find more user-friendly.
(use-package circe
:commands circe
:preface
(defun my/circe-count-nicks ()
"Display the number of users connected on the current channel."
(interactive)
(when (eq major-mode 'circe-channel-mode)
(message "%i users are online on %s."
(length (circe-channel-nicks)) (buffer-name))))
(defun my/circe-fetch-password (&rest params)
"Fetch the NickServ password for an username."
(require 'auth-source)
(let ((match (car (apply 'auth-source-search params))))
(if match
(let ((secret (plist-get match :secret)))
(if (functionp secret)
(funcall secret)
secret))
(error "[✗] Password not found for %S" params))))
(defun my/circe-nickserv-password (server)
"Fetch the NickServ password for the Libera Chat."
(my/circe-fetch-password :login "rememberYou" :machine "irc.libera.chat"))
:custom
(circe-default-part-message nil)
(circe-default-quit-message nil)
(circe-format-say (format "{nick:+%ss}: {body}" 8))
(circe-network-options
'(("Libera Chat"
:nick "rememberYou"
:tls t
:port 6697
:server-buffer-name "⇄ Libera Chat"
:channels (:after-auth "#archlinux" "#bash" "#emacs" "#linux" "#python" "#qutebrowser" "#sway")
:nickserv-password my/circe-nickserv-password)))
(circe-reduce-lurker-spam t)
(circe-use-cycle-completion t)
(lui-flyspell-p t)
:config
(circe-lagmon-mode)
(enable-circe-color-nicks)
(enable-circe-display-images))
Finally if like me you would like to be informed by desktop notifications when you are mentioned on IRC, the circe-notifications package allows you to do that.
(use-package circe-notifications
:after circe
:hook (circe-server-connected . enable-circe-notifications))
To integrate syntax checking during development, Flycheck lints warnings and errors directly within buffers. To use it, you need to install the flycheck package on GNU Emacs and install the necessary linters for the programming languages you use, via your system package manager.
NOTE: The GNU Emacs community has produced a number of extensions to Flycheck.
(use-package flycheck
:delight
:hook (lsp-mode . flycheck-mode)
:bind (:map flycheck-mode-map
("M-'" . flycheck-previous-error)
("M-\\" . flycheck-next-error))
:custom (flycheck-display-errors-delay .3))
After trying the gnus
built-in package that I found too old and notmuch
that in
my opinion lacks features like the ability to delete some emails and be able to
write emails easily with org
, I finally found my happiness with mu4e
.
I used offlineimap
, but I find it slower than mbsync
. This is why I now use
mbsync
to synchronize emails on IMAP server with local mails dir folder.
NOTE: to use mbsync
with your Gmail account, you will need to enable access
for less secure apps in your Google account.
Before that you can use this configuration, make sure to install mu
on your
operating system and to create directories corresponding to those in your
mailbox. Then, initialize the mu
database, by replacing values of --maildir
and
--my-address
with yours.
mu init --maildir ~/mails --my-address=${EMAIL}
Now all that remains is to configure mu4e
:
(use-package mu4e
:ensure nil
:ensure-system-package mu
:load-path "/usr/share/emacs/site-lisp/mu4e"
:commands mu4e
:hook (mu4e-compose-mode . turn-off-auto-fill)
:bind (:map mu4e-headers-mode-map
("M-[" . scroll-down-command)
("M-]" . scroll-up-command))
:preface
(defun my/set-email-account (label letvars)
"Registers an email address for mu4e."
(setq mu4e-contexts
(cl-loop for context in mu4e-contexts
unless (string= (mu4e-context-name context) label)
collect context))
(let ((context (make-mu4e-context
:name label
:enter-func (lambda () (mu4e-message "Switched context"))
:leave-func #'mu4e-clear-caches
:match-func
(lambda (msg)
(when msg
(string-prefix-p (format "/%s" msg)
(mu4e-message-field msg :maildir))))
:vars letvars)))
(push context mu4e-contexts)
context))
:custom
(mu4e-attachment-dir "~/downloads")
;; To avoid synchronization issues/ with mbsync
(mu4e-change-filenames-when-moving t)
(mu4e-confirm-quit nil)
(mu4e-completing-read-function 'ivy-read)
(mu4e-compose-complete-only-after (format-time-string
"%Y-%m-%d"
(time-subtract (current-time) (days-to-time 150))))
(mu4e-compose-context-policy 'ask-if-none)
(mu4e-compose-dont-reply-to-self t)
(mu4e-compose-format-flowed t)
(mu4e-get-mail-command (format "mbsync -c '%s/isync/mbsyncrc' -a" xdg-config))
(mu4e-headers-date-format "%F")
(mu4e-headers-fields
'((:account . 10)
(:human-date . 12)
(:flags . 6)
(:from . 22)
(:subject . nil)))
(mu4e-headers-time-format "%R")
(mu4e-html2text-command "iconv -c -t utf-8 | pandoc -f html -t plain")
(mu4e-maildir "~/mails")
(mu4e-org-contacts-file "~/.personal/agenda/contacts.org")
(mu4e-update-interval (* 5 60))
(mu4e-use-fancy-chars t)
(mu4e-view-prefer-html t)
(mu4e-view-show-addresses t)
(mu4e-view-show-images t)
:config
(my/set-email-account "erroriamnotfound"
'((mu4e-drafts-folder . "/personal/erroriamnotfound/drafts")
(mu4e-refile-folder . "/personal/erroriamnotfound/all")
(mu4e-sent-folder . "/personal/erroriamnotfound/sent")
(mu4e-trash-folder . "/personal/erroriamnotfound/trash")
(mu4e-maildir-shortcuts . ((:maildir "/personal/erroriamnotfound/all" :key ?a)
(:maildir "/personal/erroriamnotfound/inbox" :key ?i)
(:maildir "/personal/erroriamnotfound/drafts" :key ?d)
(:maildir "/personal/erroriamnotfound/sent" :key ?s)
(:maildir "/personal/erroriamnotfound/trash" :key ?t)))
(smtpmail-smtp-user . "[email protected]")
(smtpmail-smtp-server . "smtp.gmail.com")
(smtpmail-smtp-service . 465)
(smtpmail-stream-type . ssl)
(user-mail-address . "[email protected]")
(user-full-name . "Who Cares")))
(my/set-email-account "terencio.agozzino"
'((mu4e-drafts-folder . "/personal/terencio-agozzino/drafts")
(mu4e-refile-folder . "/personal/terencio-agozzino/all")
(mu4e-sent-folder . "/personal/terencio-agozzino/sent")
(mu4e-trash-folder . "/personal/terencio-agozzino/trash")
(mu4e-maildir-shortcuts . ((:maildir "/personal/terencio-agozzino/all" :key ?a)
(:maildir "/personal/terencio-agozzino/inbox" :key ?i)
(:maildir "/personal/terencio-agozzino/drafts" :key ?d)
(:maildir "/personal/terencio-agozzino/sent" :key ?s)
(:maildir "/personal/terencio-agozzino/trash" :key ?t)))
(smtpmail-smtp-user . "[email protected]")
(smtpmail-smtp-server . "smtp.gmail.com")
(smtpmail-smtp-service . 465)
(smtpmail-stream-type . ssl)
(user-mail-address . "[email protected]")
(user-full-name . "Terencio Agozzino")))
(setq mu4e-headers-attach-mark `("a" . ,(with-faicon "paperclip" "" 0.75 -0.05 "all-the-icons-lyellow"))
mu4e-headers-draft-mark `("D" . ,(with-octicon "pencil" "" 0.75 -0.05 "all-the-icons-lsilver"))
mu4e-headers-encrypted-mark `("x" . ,(with-faicon "lock" "" 0.75 -0.05 "all-the-icons-lred"))
mu4e-headers-flagged-mark `("F" . ,(with-faicon "flag" "" 0.75 -0.05 "all-the-icons-maroon"))
mu4e-headers-new-mark `("N" . ,(with-faicon "check-circle" "" 0.75 -0.05 "all-the-icons-silver"))
mu4e-headers-passed-mark `("P" . ,(with-faicon "share" "" 0.75 -0.05 "all-the-icons-purple "))
mu4e-headers-replied-mark `("R" . ,(with-faicon "reply" "" 0.75 -0.05 "all-the-icons-lgreen"))
mu4e-headers-seen-mark `("S" . ,(with-octicon "check" "" 1 -0.05 "all-the-icons-lgreen"))
mu4e-headers-signed-mark `("s" . ,(with-faicon "key" "" 0.75 -0.05 "all-the-icons-cyan"))
mu4e-headers-trashed-mark `("T" . ,(with-faicon "trash" "" 0.75 -0.05 "all-the-icons-lred"))
mu4e-headers-unread-mark `("u" . ,(with-faicon "envelope" "" 0.75 -0.05 "all-the-icons-silver")))
(add-to-list 'mu4e-header-info-custom
'(:account
:name "Account"
:shortname "Account"
:help "Which account this email belongs to"
:function
(lambda (msg)
(let ((maildir (mu4e-message-field msg :maildir)))
(format "%s" (substring maildir 1 (string-match-p "/" maildir 1)))))))
(add-to-list 'mu4e-headers-actions '("org-contact-add" . mu4e-action-add-org-contact) t)
(add-to-list 'mu4e-view-actions '("org-contact-add" . mu4e-action-add-org-contact) t))
To edit my emails in org-mode
, I use org-mime
. It also to add a signature,
automatically convert Org in HTML, and custom some HTML elements.
(use-package org-mime
:after mu4e
:hook (message-send . org-mime-htmlize)
:bind (:map mu4e-compose-mode-map
("C-c '" . org-mime-edit-mail-in-org-mode))
:config
(add-hook 'org-mime-html-hook (lambda ()
(goto-char (point-max))
(insert "--<br>
<strong>Terencio Agozzino</strong><br>
Data Scientist
<br>
<span style='text-decoration:none; color:#000;'>(+32) 495 45 29 07</span>")))
(add-hook 'org-mime-html-hook (lambda ()
(org-mime-change-element-style "p" (format "color: %s" "#1a1a1a"))))
(add-hook 'org-mime-html-hook (lambda ()
(org-mime-change-element-style "strong" (format "color: %s" "#000"))))
(add-hook 'org-mime-html-hook (lambda ()
(org-mime-change-element-style
"pre" "background: none repeat scroll 0% 0% rgb(61, 61, 61);
border-radius: 15px;
color: #eceff4;
font-family: Courier, 'Courier New', monospace;
font-size: small;
font-weight: 400;
line-height: 1.3em;
padding: 20px;
quotes: '«' '»';
width: 41%;")))
(setq org-mime-export-options '(:preserve-breaks t
:section-numbers nil
:with-author nil
:with-toc nil)))
Being able to read mails is a good thing, but being notified when we receive
mails is better! The following few lines allow you to receive desktop
notifications and modeline display for mu4e
:
(use-package mu4e-alert
:hook ((after-init . mu4e-alert-enable-mode-line-display)
(after-init . mu4e-alert-enable-notifications))
:config (mu4e-alert-set-default-style 'libnotify))
Before you can send mails, create the .authinfo
file if it is not already
done. Then add the following two lines replacing terencio.agozzino (which
corresponds to my [email protected] Gmail address without the domain
name) and <password> by those that match your real information:
machine imap.gmail.com login [email protected] password <password> port 993 machine smtp.gmail.com login [email protected] password <password> port 465
Similar to IRC, if you want to store your password in a GPG file, you just need
to specify a file priority list with auth-sources
, to specify to GNU Emacs where
to start looking for your password first. Then encrypt that file with gpg -c
.authinfo
and do not forget to delete the .authinfo
file.
(use-package message
:ensure nil
:after mu4e
:custom
(message-citation-line-format "On %B %e, %Y at %l:%M %p, %f (%n) wrote:\n")
(message-citation-line-function 'message-insert-formatted-citation-line)
(message-kill-buffer-on-exit t)
(message-send-mail-function 'smtpmail-send-it)
(mml-secure-openpgp-signers '("208FCDBB98190562")))
All you need to do now is to test sending your mails with C-x m
or directly from
mu4e
!
Good money management is a skill to be acquired as soon as possible. Fortunately
for us, Ledger allows you to have a double-entry accounting system directly from
the UNIX command line. To use Ledger with GNU Emacs, you need to the ledger-mode
package. However, do not forget to install the ledger
package with your system
package manager.
I discovered Ledger as a student and learning how to manage time and expenses at the same time was too much. I decided for the time being to briefly learn Ledger and focus more on time management. One issue I had with Ledger was the lack of support from my bank to export my transactions into a file type that Ledger could understand. Unfortunately, since I often made bank transfers, encoding each payment by hand was a pain. For the curious people, alternatives to Ledger exist, some of which are open-source and proprietary (e.g., YNAB).
NOTE: by default, Ledger uses the ISO 8601 format to write dates, which is the recommended format.
(use-package ledger-mode
:mode ("\\.\\(dat\\|ledger\\)\\'")
:preface
(defun my/ledger-save ()
"Clean the ledger buffer at each save."
(interactive)
(ledger-mode-clean-buffer)
(save-buffer))
:bind (:map ledger-mode-map
("C-x C-s" . my/ledger-save))
:hook (ledger-mode . ledger-flymake-enable)
:custom
(ledger-clear-whole-transactions t)
(ledger-reconcile-default-commodity "EUR")
(ledger-reports
'(("account statement" "%(binary) reg --real [[ledger-mode-flags]] -f %(ledger-file) ^%(account)")
("balance sheet" "%(binary) --real [[ledger-mode-flags]] -f %(ledger-file) bal ^assets ^liabilities ^equity")
("budget" "%(binary) --empty -S -T [[ledger-mode-flags]] -f %(ledger-file) bal ^assets:bank ^assets:receivables ^assets:cash ^assets:budget")
("budget goals" "%(binary) --empty -S -T [[ledger-mode-flags]] -f %(ledger-file) bal ^assets:bank ^assets:receivables ^assets:cash ^assets:'budget goals'")
("budget obligations" "%(binary) --empty -S -T [[ledger-mode-flags]] -f %(ledger-file) bal ^assets:bank ^assets:receivables ^assets:cash ^assets:'budget obligations'")
("budget debts" "%(binary) --empty -S -T [[ledger-mode-flags]] -f %(ledger-file) bal ^assets:bank ^assets:receivables ^assets:cash ^assets:'budget debts'")
("cleared" "%(binary) cleared [[ledger-mode-flags]] -f %(ledger-file)")
("equity" "%(binary) --real [[ledger-mode-flags]] -f %(ledger-file) equity")
("income statement" "%(binary) --invert --real -S -T [[ledger-mode-flags]] -f %(ledger-file) bal ^income ^expenses -p \"this month\""))
(ledger-report-use-header-line nil)))
Finally, to provides a flychecker
checker for Ledger files, you can use
flycheck-ledger.
(use-package flycheck-ledger :after ledger-mode)
The package menu has a simplistic interface. The paradox package improves the display of this interface, provides different mode line information and other features.
(use-package paradox
:defer 1
:custom
(paradox-column-width-package 25)
(paradox-display-star-count nil)
(paradox-execute-asynchronously t)
(paradox-hide-wiki-packages t)
:config (paradox-enable))
Managing parentheses can be painful. One of the first things you want to do is to change the appearance of the highlight of the parentheses pairs.
(use-package faces
:ensure nil
:custom (show-paren-delay 0)
:config
(set-face-background 'show-paren-match "#161719")
(set-face-bold 'show-paren-match t)
(set-face-foreground 'show-paren-match "#ffffff"))
Then, I also like to highlight brackets and other delimiters (e.g., brackets or braces) with a different color, depending on their depth. The rainbow-delimiters package allows such behavior.
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
Finally, to manage your parenthesis, I recommend to use smartparens which is in
my opinion a better alternative to paredit or to the built-in electric-pair-mode
package.
(use-package smartparens
:delight
:hook (prog-mode . smartparens-mode)
:bind (("M-'" . sp-backward-sexp)
("M-\\" . sp-forward-sexp)
("M-(" . sp-wrap-round)
("M-[" . sp-wrap-curly))
:custom (sp-escape-quotes-after-insert nil))
To get or provide help, it is important to be able to parse a whole buffer or a region of this buffer. The webpaste package allows you to parse your code using a pastebin-like service.
(use-package webpaste
:defer 0.4
:bind (("C-c C-p C-b" . webpaste-paste-buffer)
("C-c C-p C-p" . webpaste-paste-buffer-or-region)
("C-c C-p C-r" . webpaste-paste-region))
:custom (webpaste-provider-priority '("dpaste.org" "dpaste.com" "ix.io")))
Similarly, the imgbb package allows you to select and upload images using the imgbb service.
(use-package imgbb
:commands imgbb-upload
:bind ("C-c C-p C-i" . imgbb-upload))
By default, opening PDFs under GNU Emacs sucks. Fortunately, the pdf-tools
package provides pdf-view
to offer a better support for PDFs.
NOTE: If pdf-tools
fails to open a PDF, please execute the pdf-tools-install
command.
(use-package pdf-tools
:magic ("%PDF" . pdf-view-mode)
:init (pdf-tools-install :no-query))
To be comfortable with PDFs, let’s customize pdf-view which is a major mode part
of the pdf-tools
package.
(use-package pdf-view
:ensure nil
:after pdf-tools
:bind (:map pdf-view-mode-map
("C-s" . isearch-forward)
("d" . pdf-annot-delete)
("h" . pdf-annot-add-highlight-markup-annotation)
("t" . pdf-annot-add-text-annotation))
:custom
(pdf-view-display-size 'fit-page)
(pdf-view-resize-factor 1.1)
;; Avoid searching for unicodes to speed up pdf-tools.
(pdf-view-use-unicode-ligther nil)
;; Enable HiDPI support, at the cost of memory.
(pdf-view-use-scaling t))
To interact with your projects characterized by a folder with at least one
special file (e.g., a VCS marker or a project descriptor file like pom.xml
or
Gemfile
), the projectile package is a must on GNU Emacs.
(use-package projectile
:diminish (projectile-mode)
:custom
(projectile-cache-file (expand-file-name (format "%s/emacs/projectile.cache" xdg-cache)))
(projectile-enable-caching t)
(projectile-keymap-prefix (kbd "C-c C-p"))
(projectile-known-projects-file (expand-file-name (format "%s/emacs/projectile-bookmarks.eld" xdg-cache)))
(projectile-mode-line '(:eval (projectile-project-name)))
;; Define the folder containing git repositories (e.g., ~/.local/share/git).
(projectile-project-search-path '("~/.local/share/git"))
(projectile-switch-project-action #'projectile-dired)
:config (projectile-global-mode))
I also use projectile
with consult
through the counsult-projectile package.
(use-package consult-projectile
:after (consult projectile)
:straight (consult-projectile :type git :host gitlab :repo
"OlMon/consult-projectile" :branch "master")
:commands (consult-projectile))
Finally, ibuffer-projectile is helpful to group the buffers in the ibuffer
list
by projectile project.
(use-package ibuffer-projectile
:after ibuffer
:preface
(defun my/ibuffer-projectile ()
(ibuffer-projectile-set-filter-groups)
(unless (eq ibuffer-sorting-mode 'alphabetic)
(ibuffer-do-sort-by-alphabetic)))
:hook (ibuffer . my/ibuffer-projectile))
(use-package yasnippet-snippets
:after yasnippet
:config (yasnippet-snippets-initialize))
(use-package yasnippet
:delight yas-minor-mode "υ"
:hook (yas-minor-mode . my/disable-yas-if-no-snippets)
:config (yas-global-mode)
:preface
(defun my/disable-yas-if-no-snippets ()
(when (and yas-minor-mode (null (yas--get-snippet-tables)))
(yas-minor-mode -1))))
(use-package ivy-yasnippet :after yasnippet)
(use-package react-snippets :after yasnippet)
To automatically add spacing around operators as you develop, the electric-operator package is there for that.
NOTE: I don’t use electric-operator
for C-like languages which works a bit less
well according to the author of the package.
(use-package electric-operator
:hook ((css-mode java-mode js2-mode
python-mode sql-mode typescript-mode) . electric-operator-mode))
It could be useful to have easy access to recently modified files.
(use-package recentf
:ensure nil
:bind ("C-x C-r" . recentf-open-files)
:init (recentf-mode)
:custom
(recentf-exclude (list "/scp:"
"/ssh:"
"/sudo:"
"/tmp/"
"~$"
"COMMIT_EDITMSG"))
(recentf-max-menu-items 15)
(recentf-max-saved-items 200)
(recentf-save-file (expand-file-name (format "%s/emacs/recentf" xdg-cache)))
;; Save recent files every 5 minutes to manage abnormal output.
:config (run-at-time nil (* 5 60) 'recentf-save-list))
Occasionally, I would like to have a summary of a term directly on GNU Emacs, before that I would like to know more about this term. The wiki-summary package allows this behavior.
(use-package wiki-summary
:commands (wiki-summary wiki-summary-insert)
:bind ("C-c W" . wiki-summary)
:preface
(defun my/format-summary-in-buffer (summary)
"Given a summary, sticks it in the *wiki-summary* buffer and displays
the buffer."
(let ((buf (generate-new-buffer "*wiki-summary*")))
(with-current-buffer buf
(princ summary buf)
(fill-paragraph)
(goto-char (point-min))
(view-mode))
(pop-to-buffer buf)))
:config
(advice-add 'wiki-summary/format-summary-in-buffer
:override #'my/format-summary-in-buffer))
It happens that I want to try a GNU Emacs package without installing it. The try package allows this behavior.
(use-package try :commands try)
It is quite common to work on Git repositories, therefore it is important to
have a configuration that suits us. To interact with Git, GNU Emacs has magit
which is the best tool I have tested. With magit
you can directly interact with
your repositories through an interface.
(use-package magit
:commands magit-status
:custom
(magit-display-buffer-function #'magit-display-buffer-same-window-except-diff-v1))
To make sure that the summary and the body of the commits respect the
conventions, the git-commit package from magit
is perfect.
(use-package git-commit
:ensure nil
:preface
(defun my/git-commit-auto-fill-everywhere ()
"Ensure that the commit body does not exceed 72 characters."
(setq fill-column 72)
(setq-local comment-auto-fill-only-comments nil))
:hook (git-commit-mode . my/git-commit-auto-fill-everywhere)
:custom (git-commit-summary-max-length 50))
To manage diff3 conflicts with Git, the smerge-mode builtin package is helpful.
(use-package smerge-mode
:after hydra
:delight " ∓"
:commands smerge-mode
:bind (:map smerge-mode-map
("M-g n" . smerge-next)
("M-g p" . smerge-prev))
:hook (magit-diff-visit-file . hydra-merge/body))
Finally, I like to know the modified lines of a file while I edit it.
(use-package git-gutter
:defer 0.3
:delight
:config (global-git-gutter-mode))
It is annoying to see useless blank spaces at the end of a line or a file. Let’s get rid of these spaces. Most of the time, I also like to have justified texts.
(use-package simple
:ensure nil
:delight (auto-fill-function)
:preface
(defun my/kill-region-or-line ()
"When called interactively with no active region, kill the whole line."
(interactive)
(if current-prefix-arg
(progn
(kill-new (buffer-string))
(delete-region (point-min) (point-max)))
(progn (if (use-region-p)
(kill-region (region-beginning) (region-end) t)
(kill-region (line-beginning-position) (line-beginning-position
2))))))
:hook ((before-save . delete-trailing-whitespace)
((prog-mode text-mode) . turn-on-auto-fill))
:bind ("C-w" . my/kill-region-or-line)
:custom (set-mark-command-repeat-pop t))
Finally, I also like is to be able to delete every consecutive space characters when a space character is deleted. The hungry-delete package allows this behavior.
(use-package hungry-delete
:defer 0.7
:delight
:config (global-hungry-delete-mode))
Org Mode is one of my favorite modes in GNU Emacs. I mainly use it to write notes and manage my time to enjoy a better organization of my life, but you can do lots of things with it. It is like the sky, without limits.
To use Org Mode, GNU Emacs already has the org
built-in package. However, some
extra packages are in the org-contrib package. It is therefore a good idea to
install it.
(use-package org
:ensure org-contrib
:delight "Θ"
:hook (org-mode . turn-off-auto-fill)
:bind ("C-c i" . org-insert-structure-template)
:preface
(defun my/org-archive-done-tasks ()
"Archive finished or cancelled tasks."
(interactive)
(org-map-entries
(lambda ()
(org-archive-subtree)
(setq org-map-continue-from (outline-previous-heading)))
"TODO=\"DONE\"|TODO=\"CANCELLED\"" (if (org-before-first-heading-p) 'file 'tree)))
(defun my/org-jump ()
"Jump to a specific task."
(interactive)
(let ((current-prefix-arg '(4)))
(call-interactively 'org-refile)))
(defun my/org-use-speed-commands-for-headings-and-lists ()
"Activate speed commands on list items too."
(or (and (looking-at org-outline-regexp) (looking-back "^\**"))
(save-excursion (and (looking-at (org-item-re)) (looking-back "^[ \t]*")))))
(defmacro ignore-args (fnc)
"Returns function that ignores its arguments and invokes FNC."
`(lambda (&rest _rest)
(funcall ,fnc)))
:hook ((after-save . my/config-tangle)
(auto-save . org-save-all-org-buffers)
(org-mode . org-indent-mode)
(org-mode . visual-line-mode))
:custom
(org-archive-location "~/.personal/archives/%s::")
(org-blank-before-new-entry '((heading . t)
(plain-list-item . t)))
(org-confirm-babel-evaluate nil)
(org-cycle-include-plain-lists 'integrate)
(org-ellipsis " ▾")
(org-export-backends '(ascii beamer html icalendar latex man md org texinfo))
(org-hide-emphasis-markers t)
(org-log-done 'time)
(org-log-into-drawer t)
(org-modules '(org-crypt
org-habit
org-mouse
org-protocol
org-tempo))
(org-refile-allow-creating-parent-nodes 'confirm)
(org-refile-use-cache nil)
(org-refile-use-outline-path nil)
(org-refile-targets '((org-agenda-files . (:maxlevel . 1))))
(org-startup-folded nil)
(org-startup-with-inline-images t)
(org-tag-alist
'((:startgroup . "Context")
("@errands" . ?e)
("@home" . ?h)
("@work" . ?w)
(:endgroup)
(:startgroup . "Difficulty")
("easy" . ?E)
("medium" . ?M)
("challenging" . ?C)
(:endgroup)
("bug" . ?b)
("car" . ?v)
("future" . ?F)
("goal" . ?g)
("health" . ?H)
("house" . ?O)
("meeting" . ?m)
("planning" . ?p)
("phone" . ?0)
("purchase" . ?P)
("reading" . ?r)
("review" . ?R)
("study" . ?s)
("sport" . ?S)
("talk" . ?T)
("tech" . ?t)
("trip" . ?I)
("thinking" . ?i)
("update" . ?u)
("watch" . ?W)
("writing" . ?g)))
(org-tags-exclude-from-inheritance '("crypt" "project"))
(org-todo-keywords '((sequence "TODO(t)"
"STARTED(s)"
"NEXT(n)"
"SOMEDAY(.)"
"WAITING(w)""|" "DONE(x!)" "CANCELLED(c@)")))
(org-use-effective-time t)
(org-use-speed-commands 'my/org-use-speed-commands-for-headings-and-lists)
(org-yank-adjusted-subtrees t)
:config
(add-to-list 'org-global-properties '("Effort_ALL". "0:05 0:15 0:30 1:00 2:00 3:00 4:00"))
(add-to-list 'org-speed-commands '("$" call-interactively 'org-archive-subtree))
(add-to-list 'org-speed-commands '("i" call-interactively 'org-clock-in))
(add-to-list 'org-speed-commands '("o" call-interactively 'org-clock-out))
(add-to-list 'org-speed-commands '("s" call-interactively 'org-schedule))
(add-to-list 'org-speed-commands '("x" org-todo "DONE"))
(add-to-list 'org-speed-commands '("y" org-todo-yesterday "DONE"))
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
(add-to-list 'org-structure-template-alist '("sh" . "src shell"))
(add-to-list 'org-structure-template-alist '("py" . "src python"))
(advice-add 'org-deadline :after (ignore-args #'org-save-all-org-buffers))
(advice-add 'org-schedule :after (ignore-args #'org-save-all-org-buffers))
(advice-add 'org-store-log-note :after (ignore-args #'org-save-all-org-buffers))
(advice-add 'org-refile :after 'org-save-all-org-buffers)
(advice-add 'org-todo :after (ignore-args #'org-save-all-org-buffers))
;; Replace list hyphen with dot
(font-lock-add-keywords 'org-mode
'(("^ *\\([-]\\) "
(0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•"))))))
(org-clock-persistence-insinuate)
(org-load-modules-maybe t))
If like me you want to automatically update the tables of contents of your .org
files, toc-org is the ideal package. To automate these tables of contents, you
only need to use the :TOC:
tag in the first heading of these tables of contents.
(use-package toc-org
:after org
:hook (org-mode . toc-org-enable))
To indent the text in Org Mode according to the structure of the plan, the
org-indent
built-in package is fine, but I don’t want to see it loaded in the
modeline.
(use-package org-indent :ensure nil :after org :delight)
Nowadays, it is crucial to be organized. Even more than before. That is why it
is important to take the time to make a configuration that is simple to use and
that makes your life easier with an irreproachable organization. The org-agenda
built-in package allows me to be organized in my daily tasks. As a result, I can
use my time to the fullest.
NOTE: To synchronize and access my calendar via multiple devices (e.g.,
smartphone or other laptop), I synchronize my .org
files with Syncthing
(use-package org-agenda
:ensure nil
:bind (:map org-agenda-mode-map
("C-n" . org-agenda-next-item)
("C-p" . org-agenda-previous-item)
("j" . org-agenda-goto)
("X" . my/org-agenda-mark-done-next)
("x" . my/org-agenda-mark-done))
:preface
(defun my/org-agenda-mark-done (&optional arg)
"Mark the current TODO as done in org-agenda."
(interactive "P")
(org-agenda-todo "DONE"))
(defun my/org-agenda-mark-done-next ()
"Mark the current TODO as done and add another task after it."
(interactive)
(org-agenda-todo "DONE")
(org-agenda-switch-to)
(org-capture 0 "t"))
:custom
(org-agenda-category-icon-alist
`(("home" ,(list (all-the-icons-faicon "home" :v-adjust -0.05)) nil nil :ascent center :mask heuristic)
("inbox" ,(list (all-the-icons-faicon "inbox" :v-adjust -0.1)) nil nil :ascent center :mask heuristic)
("people" ,(list (all-the-icons-material "people" :v-adjust -0.25)) nil nil :ascent center :mask heuristic)
("routine" ,(list (all-the-icons-material "repeat":v-adjust -0.25)) nil nil :ascent center :mask heuristic)
))
(org-agenda-custom-commands
'(("d" "Dashboard"
((agenda "" ((org-deadline-warning-days 7)))
(todo "NEXT"
((org-agenda-overriding-header "Next Tasks")))
(tags-todo "agenda/ACTIVE" ((org-agenda-overriding-header "Active Projects")))))
("n" "Next Tasks"
((agenda "" ((org-deadline-warning-days 7)))
(todo "NEXT"
((org-agenda-overriding-header "Next Tasks")))))
("h" "Home Tasks" tags-todo "+home")
("w" "Work Tasks" tags-todo "+work")
("E" "Easy Tasks" tags-todo "+easy")
("C" "Challenging Tasks" tags-todo "+challenging")
("e" tags-todo "+TODO=\"NEXT\"+Effort<15&+Effort>0"
((org-agenda-overriding-header "Low Effort Tasks")
(org-agenda-max-todos 20)
(org-agenda-files org-agenda-files)))))
(org-agenda-dim-blocked-tasks t)
(org-agenda-files '("~/.personal/agenda"))
(org-agenda-inhibit-startup t)
(org-agenda-show-log t)
(org-agenda-skip-deadline-if-done t)
(org-agenda-skip-deadline-prewarning-if-scheduled 'pre-scheduled)
(org-agenda-skip-scheduled-if-done t)
(org-agenda-span 2)
(org-agenda-start-on-weekday 6)
(org-agenda-start-with-log-mode t)
(org-agenda-sticky nil)
(org-agenda-tags-column 90)
(org-agenda-time-grid '((daily today require-timed)))
(org-agenda-use-tag-inheritance t)
(org-columns-default-format "%14SCHEDULED %Effort{:} %1PRIORITY %TODO %50ITEM %TAGS")
(org-default-notes-file "~/.personal/agenda/inbox.org")
(org-directory "~/.personal")
(org-enforce-todo-dependencies t)
(org-habit-completed-glyph ?✓)
(org-habit-graph-column 80)
(org-habit-show-habits-only-for-today nil)
(org-habit-today-glyph ?‖)
(org-track-ordered-property-with-tag t))
Finally, I use org-wild-notifier to get notifications about TODO items in
org-agenda
.
(use-package org-wild-notifier
:after org
:custom
(alert-default-style 'libnotify)
(org-wild-notifier-notification-title "Agenda Reminder")
:config (org-wild-notifier-mode))
Let’s add prettier bullets in Org Mode with org-bullets.
(use-package org-bullets
:hook (org-mode . org-bullets-mode)
:custom (org-bullets-bullet-list '("●" "►" "▸")))
org-capture
templates saves you a lot of time when adding new entries. I use
it to quickly record tasks, ledger entries, notes and other semi-structured
information.
(use-package org-capture
:ensure nil
:preface
(defvar my/org-active-task-template
(concat "* NEXT %^{Task}\n"
":PROPERTIES:\n"
":Effort: %^{effort|1:00|0:05|0:15|0:30|2:00|4:00}\n"
":CAPTURED: %<%Y-%m-%d %H:%M>\n"
":END:") "Template for basic task.")
(defvar my/org-appointment
(concat "* TODO %^{Appointment}\n"
"SCHEDULED: %t\n") "Template for appointment task.")
(defvar my/org-basic-task-template
(concat "* TODO %^{Task}\n"
":PROPERTIES:\n"
":Effort: %^{effort|1:00|0:05|0:15|0:30|2:00|4:00}\n"
":CAPTURED: %<%Y-%m-%d %H:%M>\n"
":END:") "Template for basic task.")
(defvar my/org-contacts-template
(concat "* %(org-contacts-template-name)\n"
":PROPERTIES:\n"
":BIRTHDAY: %^{YYYY-MM-DD}\n"
":END:") "Template for a contact.")
:custom
(org-capture-templates
`(
("c" "Contact" entry (file+headline "~/.personal/agenda/contacts.org" "Friends"),
my/org-contacts-template
:empty-lines 1)
("p" "People" entry (file+headline "~/.personal/agenda/people.org" "Tasks"),
my/org-basic-task-template
:empty-lines 1)
("a" "Appointment" entry (file+headline "~/.personal/agenda/people.org" "Appointments"),
my/org-appointment
:empty-lines 1)
("m" "Meeting" entry (file+headline "~/.personal/agenda/people.org" "Meetings")
"* Meeting with %? :meeting:\n%U" :clock-in t :clock-resume t :empty-lines 1)
("P" "Phone Call" entry (file+headline "~/.personal/agenda/people.org" "Phone Calls")
"* Phone %? :phone:\n%U" :clock-in t :clock-resume t)
("i" "New Item")
("ib" "Book" checkitem (file+headline "~/.personal/items/books.org" "Books")
"- [ ] %^{Title} -- %^{Author} %? :@home:reading:\n%U"
:immediate-finish t)
("il" "Learning" checkitem (file+headline "~/.personal/items/learning.org" "Things")
"- [ ] %^{Thing} :@home:"
:immediate-finish t)
("im" "Movie" checkitem (file+headline "~/.personal/items/movies.org" "Movies")
"- [ ] %^{Title} :@home:watch:\n%U"
:immediate-finish t)
("ip" "Purchase" checkitem (file+headline "~/.personal/items/purchases.org" "Purchases")
"- [ ] %^{Item} :@home:purchase:\n%U"
:immediate-finish t)
("t" "New Task")
("ta" "Active" entry (file+headline "~/.personal/agenda/inbox.org" "Active"),
my/org-active-task-template
:empty-lines 1
:immediate-finish t)
("tb" "Backlog" entry (file+headline "~/.personal/agenda/inbox.org" "Backlog"),
my/org-basic-task-template
:empty-lines 1
:immediate-finish t))))
To estimate and track the time of my tasks, GNU Emacs provides the org-clock
built-in package.
(use-package org-clock
:ensure nil
:after org
:preface
(defun my/org-mode-ask-effort ()
"Ask for an effort estimate when clocking in."
(unless (org-entry-get (point) "Effort")
(let ((effort
(completing-read
"Effort: "
(org-entry-get-multivalued-property (point) "Effort"))))
(unless (equal effort "")
(org-set-property "Effort" effort)))))
:hook (org-clock-in-prepare-hook . my/org-mode-ask-effort)
:custom
(org-clock-clocktable-default-properties
'(:block day :maxlevel 2 :scope agenda :link t :compact t :formula %
:step day :fileskip0 t :stepskip0 t :narrow 80
:properties ("Effort" "CLOCKSUM" "CLOCKSUM_T" "TODO")))
(org-clock-continuously nil)
(org-clock-in-switch-to-state "STARTED")
(org-clock-out-remove-zero-time-clocks t)
(org-clock-persist t)
(org-clock-persist-file (expand-file-name (format "%s/emacs/org-clock-save.el" xdg-cache)))
(org-clock-persist-query-resume nil)
(org-clock-report-include-clocking-task t)
(org-show-notification-handler (lambda (msg) (alert msg))))
As a person with a short attention span, the Pomodoro technique allows me to maximize my concentration throughout the day. Personally, I do sessions of 25 minutes of concentrated work and 5 minutes of break. Finally, every two hours, I take a 30 minute break. To use the Pomodoro technique with GNU Emacs, the org-pomodoro package is suggested.
(use-package org-pomodoro
:after org
:custom
(alert-user-configuration (quote ((((:category . "org-pomodoro")) libnotify nil))))
(org-pomodoro-audio-player "/usr/bin/mpv")
(org-pomodoro-finished-sound "~/audio/pomodoro_finished.mp3")
(org-pomodoro-format " %s")
(org-pomodoro-killed-sound "~/audio/pomodoro_killed.mp3")
(org-pomodoro-long-break-sound "~/audio/pomodoro_long.mp3")
(org-pomodoro-overtime-sound "~/audio/pomodoro_overtime.mp3")
(org-pomodoro-short-break-sound "~/audio/pomodoro_short.mp3")
(org-pomodoro-start-sound "~/audio/pomodoro_start.mp3")
(org-pomodoro-start-sound-p t))
Managing your business and personal contact information can quickly turn into a
mess. Several solutions exist in GNU Emacs. Personally I use the org-contacts
package available in the org-contrib
package.
(use-package org-contacts
:after org
:ensure nil
:custom (org-contacts-files '("~/.personal/agenda/contacts.org")))
Let’s change the foreground and the weight of each keyword.
(use-package org-faces
:ensure nil
:custom
(org-todo-keyword-faces
'(("DONE" . (:foreground "#8abeb7" :weight bold))
("NEXT" . (:foreground "#f0c674" :weight bold))
("SOMEDAY" . (:foreground "#b294bb" :weight bold))
("TODO" . (:foreground "#b5bd68" :weight bold))
("WAITING" . (:foreground "#de935f" :weight bold)))))
To encrypt and decrypt GPG files with org-mode
, you need to add org-crypt
in
your org-modules
, install gnupg
with your system manager package and create a
GPG key pair with the public key specified to org-crypt
.
(use-package org-crypt
:ensure nil
:init (org-crypt-use-before-save-magic)
:custom (org-crypt-key "E9AADC36E94A672D1A07D49B208FCDBB98190562"))
(setq epa-file-encrypt-to "[email protected]")
(setq epa-file-select-keys "auto")
Let’s specify languages extra to Org Mode to compile them directly into .org
files.
(use-package jupyter :ensure nil :after org)
(use-package python :ensure nil :after org)
(use-package ob-C :ensure nil :after org)
(use-package ob-css :ensure nil :after org)
(use-package ob-dot :ensure nil :after org)
(use-package ob-ein :ensure nil :after org)
(use-package ob-emacs-lisp :ensure nil :after org)
(use-package ob-gnuplot :ensure nil :after org)
(use-package ob-java :ensure nil :after org)
(use-package ob-js :ensure nil :after org)
(use-package ob-latex
:ensure nil
:after org
:custom (org-latex-compiler "xelatex"))
(use-package ob-ledger :ensure nil :after org)
(use-package ob-makefile :ensure nil :after org)
(use-package ob-org :ensure nil :after org)
(use-package ob-plantuml
:ensure nil
:after org
:custom (org-plantuml-jar-path (expand-file-name (format "%s/plantuml.jar" xdg-lib))))
(use-package ob-python :ensure nil :after org)
(use-package ob-shell :ensure nil :after org)
(use-package ob-sql :ensure nil :after org)
Taking notes is a necessity in a world full of information. To take these notes and interconnect them, the org-roam package is great.
After using org-journal, I also decided to use org-roam
to write my journal.
If you don’t already do so, I advise you to start writing your journal. If you haven’t done so yet, I suggest you start writing your journal. This journal would allow you, for example, to improve your mental clarity, follow your evolution, and facilitate your personal growth.
(use-package org-roam
:after org
:init
(setq org-roam-v2-ack t)
(setq my/daily-note-filename "%<%Y-%m-%d>.org.gpg"
my/daily-note-header "#+title: %<%Y-%m-%d %a>\n\n[[roam:%<%Y-%B>]]\n\n")
:custom
(org-roam-capture-templates
'(("d" "default" plain "%?"
:if-new (file+head "%<%Y%m%d%H%M%S>-${slug}.org"
"#+title: ${title}\n")
:unnarrowed t)))
(org-roam-completion-everywhere t)
(org-roam-dailies-directory "journal/")
(org-roam-dailies-capture-templates
`(("d" "default" plain
"* %?"
:if-new (file+head ,my/daily-note-filename
,my/daily-note-header)
:empty-lines 1)
("j" "journal" plain
"** %<%I:%M %p> :journal:\n\n%?\n\n"
:if-new (file+head+olp ,my/daily-note-filename
,my/daily-note-header
("Journal"))
:empty-lines 1)
("m" "meeting" entry
"** %<%I:%M %p> - %^{Meeting Title} :meeting:\n\n%?\n\n"
:if-new (file+head+olp ,my/daily-note-filename
,my/daily-note-header
("Meetings"))
:empty-lines 1)))
(org-roam-directory "~/.personal/notes")
:custom (org-roam-graph-viewer "/usr/bin/qutebrowser")
:config (org-roam-setup))