- About
- Initialize Emacs
- Start Up
- Languages
- Advanced Configuration
- Alert
- Auto-Completion
- Backups
- Buffers
- Button Lock
- Calculator
- Calendar
- Dashboard
- Deft
- Dired
- EditorConfig
- Ending Up
- EPUB
- General
- Highlight indentation
- History
- Hydra
- Icons
- IRC
- Ivy
- Keybindings
- Linters
- Lorem Ipsum
- Navigation
- Notmuch
- Parentheses
- Paste
- Point and Region
- Projectile
- Recent Files
- Requests
- Smart Mx
- Spelling
- Terminal
- Useful Tweaks
- Version Control
- Weblorg
- Whitespace
- Windows
- Word Wrap
- YASnippet
- Zetteldeft
- Org-Mode
Welcome to my literate Emacs configuration file.
This configuration was inspired by (read: large swathes copied from)
- rememberYou (
rememberYou
), - John Wiegley (
jwiegley
), - Sacha Chua (
sachac
), - Mathieu Marques (
angrybacon
).
Configure package sources.
(add-to-list 'package-archives
'("melpa" . "http://melpa.org/packages/") t)
To ease package installation use-package
is necessary so willl need to be
installed if it isn’t already.
(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)
Make sure Emacs uses all the correct environment variables
(use-package exec-path-from-shell
:ensure t
:init
(exec-path-from-shell-initialize))
The following values are set for a better default user experience.
(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 ; Prefers 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 ; Prefers 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 "Tobias Backer Dirks" ; Set the full name of the current user
user-mail-address "omgitsaheadcrab@gmail.com" ; Set the email address of the current user
display-line-numbers t ; Display line numbers on the left
vc-follow-symlinks t ; Always follow the symlinks
view-read-only t) ; Always open read-only buffers in view-mode
(cd "~/") ; Move to the user directory
(column-number-mode 1) ; Show the column number
(display-time-mode 1) ; Enable time in the mode-line
(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
(put 'narrow-to-region 'disabled nil) ; Enable region narrowing/widening
(put 'upcase-region 'disabled nil) ; Enable region upcasing
(put 'downcase-region 'disabled nil) ; Enable region downcasing
The XDG base directory specification is followed.
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: the following XDG environment variables must be set in .profile
.
(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.")
To avoid overloading init.el
file generated by the user using the UI,
the generated code is added to a separate file.
The XDG base directory specification is also followed for the auto-save-file
folder.
(setq-default
auto-save-list-file-name (expand-file-name (format "%s/emacs/auto-save-list" xdg-data))
custom-file (expand-file-name (format "%s/emacs/custom.el" xdg-data)))
(when (file-exists-p custom-file)
(load custom-file t))
Set default font to Souce Code Pro Medium.
(set-face-attribute 'default nil :font "Source Code Pro Medium")
(set-fontset-font t 'latin "Noto Sans")
lsp-mode (Language Server Protocol) is used to minimize the required configuration for programming language support. Compatible servers can be found on the lsp-mode page.
(use-package lsp-mode
:config
(lsp-register-custom-settings
'(("pylsp.plugins.pylsp_mypy.enabled" t t)
;; disable live mode to fix imports
("pylsp.plugins.pylsp_mypy.live_mode" nil t)
;; Disable these as they're duplicated by flake8
("pylsp.plugins.pycodestyle.enabled" nil t)
("pylsp.plugins.mccabe.enabled" nil t)
("pylsp.plugins.pyflakes.enabled" nil t)))
:hook ((c-mode c++-mode dockerfile-mode java-mode json-mode python-mode rust-mode typescript-mode xml-mode yaml-mode) . lsp)
:custom
(lsp-clients-typescript-server-args '("--stdio" "--tsserver-log-file" "/dev/stderr"))
(lsp-enable-folding nil)
(lsp-enable-links nil)
(lsp-enable-snippet nil)
(lsp-enable-on-type-formatting nil)
(lsp-prefer-flymake nil)
(lsp-pylsp-plugins-flake8-enabled t)
(lsp-clients-clangd-args '("-j=4" "-background-index" "-log=error" "--clang-tidy"))
(lsp-session-file (expand-file-name (format "%s/emacs/lsp-session-v1" xdg-data)))
(lsp-restart 'auto-restart)
(lsp-rust-analyzer-server-display-inlay-hints t)
(lsp-rust-analyzer-display-lifetime-elision-hints-enable "skip_trivial")
(lsp-rust-analyzer-display-chaining-hints t)
(lsp-rust-analyzer-display-lifetime-elision-hints-use-parameter-names nil)
(lsp-rust-analyzer-display-closure-return-type-hints t)
(lsp-rust-analyzer-display-parameter-hints nil)
(lsp-rust-analyzer-display-reborrow-hints nil)
)
(use-package lsp-ui)
(use-package dap-mode
:defer
:custom
(dap-auto-configure-mode t "Automatically configure dap.")
(dap-auto-configure-features
'(sessions locals breakpoints expressions tooltip) "Remove the button panel in the top.")
:config
;;; dap for c++
(require 'dap-lldb)
;;; dap for Rust
(require 'dap-cpptools)
;;; set the debugger executable (c++)
(setq dap-lldb-debug-program '("/usr/bin/lldb-vscode"))
;;; ask user for executable to debug if not specified explicitly (c++)
(setq dap-lldb-debugged-program-function (lambda () (read-file-name "Select file to debug.")))
;;; default debug template for (c++)
(dap-register-debug-template
"C++ LLDB dap"
(list :type "lldb-vscode"
:cwd nil
:args nil
:request "launch"
:program nil))
(defun dap-debug-create-or-edit-json-template ()
"Edit the C++ debugging configuration or create + edit if none exists yet."
(interactive)
(let ((filename (concat (lsp-workspace-root) "/launch.json"))
(default "~/.config/emacs/dap-default-launch.json"))
(unless (file-exists-p filename)
(copy-file default filename))
(find-file-existing filename))))
(use-package all-the-icons
:if (display-graphic-p)
;; :init (all-the-icons-install-fonts t)
:config
(add-to-list 'all-the-icons-mode-icon-alist
'(vterm-mode all-the-icons-octicon "terminal" :v-adjust 0.2))
(add-to-list 'all-the-icons-icon-alist
'("\\.xpm$" all-the-icons-octicon "file-media" :v-adjust 0.0 :face all-the-icons-dgreen))
(add-to-list 'all-the-icons-icon-alist
'("\\.toml$" all-the-icons-octicon "settings" :v-adjust 0.0 :face all-the-icons-dyellow))
(add-to-list 'all-the-icons-mode-icon-alist
'(conf-toml-mode all-the-icons-octicon "settings" :v-adjust 0.0 :face all-the-icons-dyellow))
(add-to-list 'all-the-icons-icon-alist
'("\\.lua$" all-the-icons-fileicon "lua" :face all-the-icons-dblue))
(add-to-list 'all-the-icons-mode-icon-alist
'(lua-mode all-the-icons-fileicon "lua" :face all-the-icons-dblue))
(add-to-list 'all-the-icons-mode-icon-alist
'(help-mode all-the-icons-faicon "info-circle" :height 1.1 :v-adjust -0.1 :face all-the-icons-purple))
(add-to-list 'all-the-icons-mode-icon-alist
'(helpful-mode all-the-icons-faicon "info-circle" :height 1.1 :v-adjust -0.1 :face all-the-icons-purple))
(add-to-list 'all-the-icons-mode-icon-alist
'(Info-mode all-the-icons-faicon "info-circle" :height 1.1 :v-adjust -0.1))
(add-to-list 'all-the-icons-icon-alist
'("NEWS$" all-the-icons-faicon "newspaper-o" :height 0.9 :v-adjust -0.2))
(add-to-list 'all-the-icons-icon-alist
'("Cask\\'" all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.2 :face all-the-icons-blue))
(add-to-list 'all-the-icons-mode-icon-alist
'(cask-mode all-the-icons-fileicon "elisp" :height 1.0 :v-adjust -0.2 :face all-the-icons-blue))
(add-to-list 'all-the-icons-icon-alist
'(".*\\.ipynb\\'" all-the-icons-fileicon "jupyter" :height 1.2 :face all-the-icons-orange))
(add-to-list 'all-the-icons-mode-icon-alist
'(ein:notebooklist-mode all-the-icons-faicon "book" :face all-the-icons-orange))
(add-to-list 'all-the-icons-mode-icon-alist
'(ein:notebook-mode all-the-icons-fileicon "jupyter" :height 1.2 :face all-the-icons-orange))
(add-to-list 'all-the-icons-mode-icon-alist
'(ein:notebook-multilang-mode all-the-icons-fileicon "jupyter" :height 1.2 :face all-the-icons-orange))
(add-to-list 'all-the-icons-icon-alist
'("\\.epub\\'" all-the-icons-faicon "book" :height 1.0 :v-adjust -0.1 :face all-the-icons-green))
(add-to-list 'all-the-icons-mode-icon-alist
'(nov-mode all-the-icons-faicon "book" :height 1.0 :v-adjust -0.1 :face all-the-icons-green))
(add-to-list 'all-the-icons-mode-icon-alist
'(gfm-mode all-the-icons-octicon "markdown" :face all-the-icons-lblue)))
;; While we're at it: Make dired, ‘dir’ectory ‘ed’itor, look pretty
(use-package all-the-icons-dired
:hook (dired-mode . all-the-icons-dired-mode))
(use-package pretty-mode
:config
(global-pretty-mode 0))
Use nord-theme with doom-modeline.
NOTE: to be able to see icons in the doom-modeline
, you will need to install
all-the-icons.
(use-package doom-themes
:config (load-theme 'doom-gruvbox t))
(use-package doom-modeline
:defer 0.1
:config (doom-modeline-mode))
(use-package fancy-battery
:after doom-modeline
:hook (after-init . fancy-battery-mode))
(use-package solaire-mode
:custom (solaire-mode-remap-fringe t)
:config
(solaire-global-mode +1))
(menu-bar-mode -1) ; Disable the menu bar
(scroll-bar-mode -1) ; Disable the scroll bar
(tool-bar-mode -1) ; Disable the tool bar
(tooltip-mode -1) ; Disable the tooltips
In order to have a fast and stable environment, I recommend using LSP as a client for LSP servers ccls and as server.
To use ccls
with GNU Emacs, you must first install it with the package manager
of your operating system.
(use-package ccls
:after projectile
:ensure-system-package ccls
: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"))
(setq c-basic-offset 4)
(eval-after-load "cc-mode"
(progn
(require 'cc-mode)
(put 'c-electric-paren 'delete-selection nil)
(put 'c-electric-brace 'delete-selection nil)))
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 and to generate the compile.commands.json
file, you have
to do:
cmake -H. -BDebug -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=YES
ln -s Debug/compile_commands.json
Code formatting with clang-format
(use-package clang-format)
; :after ccls)
Google style
(use-package google-c-style
:hook ((c-mode c++-mode) . google-set-c-style)
(c-mode-common . google-make-newline-indent)
:commands google-set-c-style)
CMake is a cross-platform build system generator.
(use-package cmake-mode
:mode ("CMakeLists\\.txt\\'" "\\.cmake\\'"))
(use-package cmake-font-lock
:after (cmake-mode)
:hook (cmake-mode . cmake-font-lock-activate))
In order to have a fast and stable environment, I recommend using LSP as a client for LSP servers and vscode-css-languageserver-bin as server.
(use-package css-mode
:custom (css-indent-offset 2))
(use-package less-css-mode
:mode "\\.less\\'")
(use-package scss-mode
:mode "\\.scss\\'")
(use-package csv-mode)
I like to use Docker when I need to install various databases or other services that only work on a particular operating system while keeping my operating system clean.
(use-package dockerfile-mode
:delight "δ "
:mode "Dockerfile\\'")
(use-package elisp-mode :ensure nil :delight "ξ ")
Provides minibuffer hints when working with Emacs Lisp.
(use-package eldoc
:delight
:hook (emacs-lisp-mode . eldoc-mode))
For arch install the sbcl and quicklisp packages.
sudo pacman -S sbcl
yay -S quicklisp
echo '(load "/usr/lib/quicklisp/setup")' > ~/.sbclrc
Then within sbcl install SLIME
(quicklisp-quickstart:install)
(ql:quickload "quicklisp-slime-helper")
(if (string-equal system-type "darwin")
(progn
(print "FIXME: CommonLisp (ros) on MacOS"))
;(add-to-list 'exec-path "/usr/local/bin/")
;(load (expand-file-name "~/.roswell/helper.el")))
(progn
(load (expand-file-name "/usr/lib/quicklisp/slime-helper.el"))
;; Replace "sbcl" with the path to your implementation
(setq inferior-lisp-program "sbcl")))
For arch install the guile package.
sudo pacman -S guile
(use-package geiser
:config
(setq geiser-active-implementations '(guile)))
In order to have a fast and stable environment, I recommend using LSP as a client for LSP servers and vscode-html-languageserver as server.
To use vscode-html-languageserver
with GNU Emacs, you must first install it
with the package manager of your operating system.
Let’s configure emmet-mode
, to produce HTML from CSS-like selector:
(use-package emmet-mode
:delight
:hook (css-mode sgml-mode web-mode))
ini-mode
does a good job of handling .ini
files.
(use-package ini-mode
:defer 0.4
:mode ("\\.ini\\'"))
In order to have a fast and stable environment, I recommend using lsp-java as LSP client and Eclipse JDT Language Server as LSP server.
NOTE: before configuring lsp-java
, don’t forget to configure lsp-mode.
Let’s define the LSP client to use the LSP server:
(use-package lsp-java
:after lsp
:hook (java-mode . lsp)
:custom (lsp-java-server-install-dir
(expand-file-name (format "%s/eclipse.jdt.ls/server" xdg-lib))))
(require 'dap-java)
Most of my Java projects are made with gradle
. The configuration is easy
enough:
(use-package gradle-mode
:mode ("\\.java\\'" "\\.gradle\\'")
:bind (:map gradle-mode-map
("C-c C-c" . gradle-build)
("C-c C-t" . gradle-test))
:preface
(defun my/switch-to-compilation-window ()
"Switches to the *compilation* buffer after compilation."
(other-window 1))
:config
(advice-add 'gradle-build :after #'my/switch-to-compilation-window)
(advice-add 'gradle-test :after #'my/switch-to-compilation-window))
For my JavaScript configuration, I took my sources from the Nicolas Petton’s blog which I found very well explained.
Setting up Emacs for JavaScript (part #1) Setting up Emacs for JavaScript (part #2)
I like to use prettier to get my TypeScript code clean. To use it, don’t 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")))
By default, GNU Emacs uses js-mode
as major mode for JavaScript buffers and I
prefer use js2-mode
instead because of his abilities to parses buffers and
builds an AST for things like syntax highlighting.
(use-package js2-mode
:hook ((js2-mode . js2-imenu-extras-mode)
(js2-mode . prettier-js-mode))
:mode "\\.js\\'"
:custom (js-indent-level 2))
Provides powerful refactoring based on the AST generated by js2-mode
.
(use-package js2-refactor
:bind (:map js2-mode-map
("C-k" . js2r-kill)
("M-." . nil))
:hook ((js2-mode . js2-refactor-mode)
(js2-mode . (lambda ()
(add-hook 'xref-backend-functions #'xref-js2-xref-backend nil t))))
:config (js2r-add-keybindings-with-prefix "C-c C-r"))
Makes it easy to jump to function references or definitions.
(use-package xref-js2 :defer 5)
Parses JavaScript files in a project and makes type inference to provide meaningful completion (with type clues) and cross-reference support.
Unfortunately, tern
has some problems with cross-references that explain why I
am using xref-js2
instead.
(use-package tern
:ensure-system-package (tern . "npm install -g tern")
:bind (("C-c C-c" . compile)
:map tern-mode-keymap
("M-." . nil))
:hook ((js2-mode . company-mode)
(js2-mode . tern-mode)))
Then, add a .tern-project
file to the root of your project.
Here is an example configuration for a project that uses requirejs
and
jQuery
, without taking into account of the bower_components
directory:
{
"libs": [
"jquery"
],
"loadEagerly": [
"./**/*.js"
],
"dontLoad": [
"./bower_components/"
],
"plugins": {
"requirejs": {
"baseURL": "./"
}
}
}
JSON is used a lot, especially in the web. Therefore, it is important to have a decent configuration to feel comfortable when handling such files.
(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)
"Prints the arrays of numbers in one line."
(let* ((json-encoding-pretty-print
(and json-encoding-pretty-print
(not (cl-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))
I use LaTeX for my reports, CVs, summaries, etc.
(use-package tex
:ensure auctex
: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))
:hook (LaTeX-mode . reftex-mode)
:preface
(defun my/switch-to-help-window (&optional ARG REPARSE)
"Switches to the *TeX Help* buffer after compilation."
(other-window 1))
: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))
(use-package bibtex
:after auctex
:hook (bibtex-mode . my/bibtex-fill-column)
:preface
(defun my/bibtex-fill-column ()
"Ensures that each entry does not exceed 120 characters."
(setq fill-column 120)))
(use-package company-auctex
:after (auctex company)
:config (company-auctex-init))
(use-package company-math :after (auctex company))
I want a TeX engine that can deal with Unicode and use any font I like.
(setq-default TeX-engine 'xetex)
Minor mode with distinct support for \label, \ref and \cite in LaTeX.
(use-package reftex
:after auctex
:custom
(reftex-plug-into-AUCTeX t)
(reftex-save-parse-info t)
(reftex-use-multiple-selection-buffers t))
I rarely program in Lua, but when I do, lua-mode
satisfies me amply.
(use-package lua-mode
:delight "Λ "
:mode "\\.lua\\'"
:interpreter ("lua" . lua-mode))
Before you can use this package, make sure you install pandoc
on your
operating system.
(use-package markdown-mode
:ensure-system-package (pandoc . "yay -S pandoc")
:delight "μ "
:mode ("\\.markdown\\'" "\\.md\\'")
:custom (markdown-command "/usr/bin/pandoc"))
(use-package markdown-preview-mode
:after markdown-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 use pylsp as my LSP client. I use black to reformat my Python buffer. Before use it, don’t forget to
install python-black
in your system.
Also, you’ll need python-lsp-server[all]
, pylsp-black
, pylsp-isort
, pylsp-mypy
, mypy
,
flake8
.
To sort my Python imports, py-isort does a good job. Also, don’t forget to
install python-isort
in your system.
Automagically resolve imports (requires importmagic
and epc
)
pip install python-lsp-server[all]
pip install black black-macchiato debugpy epc flake8 futureimportmagic isort
pip install mypy pdbpp pyls-flake8 pyls-isort pyls-memestra pylsp-mypy
pip install pylsp-rope python-lsp-black
Let’s take a look to my Python configuration:
(use-package python
:delight "π "
:preface
(defun python-remove-unused-imports()
"Removes 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."))))
(use-package python-black
:demand t
:after python
:config (setq python-black-extra-args '("-l 79")))
(use-package py-isort
:after python
:hook ((python-mode . pyvenv-mode)))
(use-package pyenv-mode
:after python
:hook ((python-mode . pyenv-mode)
(projectile-switch-project . projectile-pyenv-mode-set))
:custom (pyenv-mode-set "3.9.0")
: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)))))
(add-hook 'pyenv-mode-hook 'lsp)
;; Show flake8 errors in lsp-ui
(defun lsp-set-cfg ()
(let ((lsp-cfg `(:pylsp (:configurationSources ("flake8")))))
(lsp--set-configuration lsp-cfg)))
;; Activate that after lsp has started
;; (add-hook 'lsp-after-initialize-hook 'lsp-set-cfg)
(add-hook 'python-mode-hook 'lsp-set-cfg)
(use-package pyvenv
:after python
:hook ((python-mode . pyvenv-mode)
(python-mode . (lambda ()
(if-let ((pyvenv-directory (find-pyvenv-directory (buffer-file-name))))
(pyvenv-activate pyvenv-directory))
(lsp))))
:custom
(pyvenv-default-virtual-env-name "env")
(pyvenv-mode-line-indicator '(pyvenv-virtual-env-name ("[venv:"
pyvenv-virtual-env-name "]")))
:preface
(defun find-pyvenv-directory (path)
"Checks if a pyvenv directory exists."
(cond
((not path) nil)
((file-regular-p path) (find-pyvenv-directory (file-name-directory path)))
((file-directory-p path)
(or
(seq-find
(lambda (path) (file-regular-p (expand-file-name "pyvenv.cfg" path)))
(directory-files path t))
(let ((parent (file-name-directory (directory-file-name path))))
(unless (equal parent path) (find-pyvenv-directory parent))))))))
(use-package importmagic
:config
(add-hook 'python-mode-hook 'importmagic-mode))
(require 'dap-python)
(setq dap-python-debugger 'debugpy)
rustic is an extension of rust-mode which adds a number of useful features (see the its github readme) to it. It is the core of the setup and you can use just it without any other Emacs packages (and without rust-analyzer) if you just want code highlighting, compilation and cargo commands bound to emacs shortcuts, and a few other features.
(use-package rust-mode)
(use-package rustic
:bind (:map rustic-mode-map
("M-j" . lsp-ui-imenu)
("M-?" . lsp-find-references)
("C-c C-c l" . flycheck-list-errors)
("C-c C-c a" . lsp-execute-code-action)
("C-c C-c r" . lsp-rename)
("C-c C-c q" . lsp-workspace-restart)
("C-c C-c Q" . lsp-workspace-shutdown)
("C-c C-c s" . lsp-rust-analyzer-status))
:config
;; uncomment for less flashiness
;; (setq lsp-eldoc-hook nil)
;; (setq lsp-enable-symbol-highlighting nil)
;; (setq lsp-signature-auto-activate nil)
;; comment to disable rustfmt on save
(setq rustic-format-on-save nil)
(add-hook 'rustic-mode-hook 'rk/rustic-mode-hook))
(defun rk/rustic-mode-hook ()
;; so that run C-c C-c C-r works without having to confirm, but don't try to
;; save rust buffers that are not file visiting. Once
;; https://github.com/brotzeit/rustic/issues/253 has been resolved this should
;; no longer be necessary.
(when buffer-file-name
(setq-local buffer-save-without-query t)))
A recent thing when you create/edit a shell script file is to automatically
grant it execution rights (with chmod +x
).
The snippet below ensures that the execution right 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))
sql-indent
gives me the possibility to easily manage .sql
files.
(use-package sql-indent
:after (:any sql sql-interactive-mode)
:delight sql-mode "Σ ")
If you use GNU Emacs 27+, I recommend to use typescript-language-server as LSP
server. After installed it with your package manager, you need to use
typescript-mode
to get the syntax color:
(use-package typescript-mode
:mode ("\\.ts\\'" "\\.tsx\\'")
:hook (typescript-mode . prettier-js-mode)
:custom
(add-hook 'typescript-mode-hook #'(lambda ()
(enable-minor-mode
'("\\.tsx?\\'" . prettier-js-mode)))))
Unfortunately, XML is still used, especially for creating web services in SOAP.
However, xml-mode
exists to help us:
(use-package xml-mode
:ensure nil
:mode ("\\.wsdl\\'" "\\.xsd\\'"))
yaml-mode
gives me the possibility to easily manage .yml
files.
(use-package yaml-mode
:delight "ψ "
:mode "\\.yml\\'"
:interpreter ("yml" . yaml-mode)
:config
(add-hook 'yaml-mode-hook
'(lambda ()
(define-key yaml-mode-map "\C-m" 'newline-and-indent))))
Most packages use alerts
to make notifications with libnotify
. Don’t forget
to first install a notification daemon, like dunst
.
Alert is a Growl-workalike for Emacs which uses a common notification interface and multiple, selectable “styles”, whose use is fully customizable by the user.
(use-package alert
:defer 1
:custom (alert-default-style 'libnotify))
company
provides auto-completion at point and displays a small pop-in
containing the candidates.
Company is a text completion framework for Emacs. The name stands for “complete anything”. It uses pluggable back-ends and front-ends to retrieve and display completion candidates.
(use-package company
:defer 0.5
:delight
:custom
(company-begin-commands '(self-insert-command))
(company-idle-delay 0.5)
(company-minimum-prefix-length 2)
(company-show-numbers t)
(company-tooltip-align-annotations 't)
(company-dabbrev-downcase nil)
(global-company-mode t))
I use company
with company-box
that allows a company front-end with icons.
(use-package company-box
:after company
:delight
:hook (company-mode . company-box-mode))
It is important to have a stable backup environment. Don’t hesitate to save a lot.
NOTE: the functions defined below avoid running a bash command when saving certain files with GNU Emacs.
(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)
:custom
(backup-directory-alist `(("." . ,(expand-file-name (format "%s/emacs/backups/" xdg-data)))))
(delete-old-versions -1)
(vc-make-backup-files t)
(version-control t))
Buffers can quickly become a mess. For some people, it’s not a problem, but I like being able to find my way easily.
(use-package ibuffer
:bind ("C-x C-b" . ibuffer)
:config
(setq ibuffer-saved-filter-groups
'(("default"
("ERC" (mode . erc-mode))
)))
(add-hook 'ibuffer-mode-hook
#'(lambda ()
(ibuffer-switch-to-saved-filter-groups "default"))))
(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))
;; You probably don't want to see empty project groups
(setq ibuffer-show-empty-filter-groups nil)
Some buffers should not be deleted by accident:
(defvar *protected-buffers* '("*scratch*" "*Messages*")
"Buffers that cannot be killed.")
(defun my/protected-buffers ()
"Protects some buffers from being killed."
(dolist (buffer *protected-buffers*)
(with-current-buffer buffer
(emacs-lock-mode 'kill))))
(add-hook 'after-init-hook #'my/protected-buffers)
Enable button lock mode
(use-package button-lock
:config (global-button-lock-mode 1))
(defun test-link ()
(interactive)
(button-lock-set-button
"test\\?name=[a-zA-Z0-9_\\.]*"
(lambda ()
(interactive)
(browse-url (concat "http://localhost:8080/"
(buffer-substring
(previous-single-property-change (point) 'mouse-face)
(next-single-property-change (point) 'mouse-face)))))
:face 'link
:face-policy 'prepend
:keyboard-binding "RET"))
(add-hook 'python-mode-hook
(lambda () (test-link)))
May be useful in a timely manner.
(use-package calc
:defer t
: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")))
(math-units-table nil))
Remembering all the dates is not obvious, especially since some varies every year. In order to remember each important date, I recorded the list of important dates according to my country, Belgium. It is very likely that some dates are different in your country, therefore, adapt the configuration below accordingly.
(use-package calendar
:ensure nil
:custom (calendar-mark-holidays-flag t))
(use-package holidays
:ensure nil
:custom
(holiday-bahai-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-hebrew-holidays nil)
(holiday-islamic-holidays nil)
(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 3 "Father's Day")))
(holiday-oriental-holidays nil))
Always good to have a dashboard.
(use-package dashboard
:if (< (length command-line-args) 2)
:preface
(defun dashboard-load-packages (list-size)
(insert (make-string (ceiling (max 0 (- dashboard-banner-length 38)) 5) ? )
(format "%d packages loaded in %s" (length package-activated-list) (emacs-init-time))))
:custom
(dashboard-center-content t)
(dashboard-items '((recents . 10)
(bookmarks . 2)
(projects . 4)
(agenda . 5)))
(dashboard-set-file-icons t)
(dashboard-set-heading-icons t)
(dashboard-set-init-info nil)
(dashboard-set-navigator t)
(dashboard-startup-banner 'logo)
:config
(add-to-list 'dashboard-item-generators '(packages . dashboard-load-packages))
(setq initial-buffer-choice (lambda () (get-buffer "*dashboard*")))
(add-hook 'dashboard-mode-hook (lambda () (display-line-numbers-mode -1)))
(dashboard-setup-startup-hook))
Deft is an Emacs mode for quickly browsing, filtering, and editing directories of plain text notes.
(use-package deft
:bind ("<f8>" . deft)
:init (setq deft-directory "~/Documents/org/notes/"
deft-extensions '("org" "md" "txt")
deft-default-extension "org"
deft-recursive t
deft-auto-save-interval 0))
For those who didn’t know, GNU Emacs is also a file explorer.
(use-package dired
:ensure nil
:delight "Dired "
:custom
(dired-auto-revert-buffer t)
(dired-dwim-target t)
(dired-hide-details-hide-symlink-targets nil)
(dired-listing-switches "-alh")
(dired-ls-F-marks-symlinks nil)
(dired-recursive-copies 'always))
(use-package dired-narrow
:bind (("C-c C-n" . dired-narrow)
("C-c C-f" . dired-narrow-fuzzy)
("C-c C-r" . dired-narrow-regexp)))
(use-package dired-subtree
:bind (:map dired-mode-map
("<backtab>" . dired-subtree-cycle)
("<tab>" . dired-subtree-toggle)))
EditorConfig helps maintain consistent coding styles for multiple developers working on the same project across various editors and IDEs.
(use-package editorconfig
:defer 0.3
:config (editorconfig-mode 1))
I’m using an .org
file to maintain my GNU Emacs configuration. However, at its
launch, it will load the config.el
source file for a faster loading.
The code below, executes org-babel-tangle
asynchronously when
config.org
is saved.
(use-package async)
(defvar *config-file* (expand-file-name "config.org" user-emacs-directory)
"The configuration file.")
(defvar *config-last-change* (nth 5 (file-attributes *config-file*))
"Last modification time of the configuration file.")
(defvar *show-async-tangle-results* nil
"Keeps *emacs* async buffers around for later inspection.")
(defun my/config-updated ()
"Checks 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/config-tangle ()
"Tangles 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/async-babel-tangle (org-file)
"Tangles 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 "SUCCESS: %s successfully tangled (%.2fs)."
,org-file
(float-time (time-subtract (current-time)
',init-tangle-start-time)))
(message "ERROR: %s as tangle failed." ,org-file)))))))
(use-package nov
:mode ("\\.epub\\'" . nov-mode)
:custom (nov-text-width 75))
To generate a fast and quality graphic, gnuplot
is perfect.
(use-package gnuplot
:ensure-system-package gnuplot
:defer 2)
(use-package gnuplot-mode
:after gnuplot
:mode "\\.gp\\'")
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))
Improved GNU Emacs standard package menu.
Project for modernizing Emacs’ Package Menu. With improved appearance, mode-line information. Github integration, customizability, asynchronous upgrading, and more.
(use-package paradox
:defer 1
:custom
(paradox-column-width-package 27)
(paradox-column-width-version 13)
(paradox-execute-asynchronously t)
(paradox-hide-wiki-packages t)
:config
(paradox-enable)
(remove-hook 'paradox-after-execute-functions #'paradox--report-buffer-print))
Colorize colors as text with their value.
(use-package rainbow-mode
:delight
:hook (prog-mode))
Avoids call the function or reload Emacs.
(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 1))
Useful to temporary use a package.
(use-package try :defer 5)
GNU Emacs’s undo system allows you to recover any past state of a buffer. To do this, Emacs treats “undo itself as another editing that can be undone”.
(use-package undo-tree
:delight
:bind ("C--" . undo-tree-redo)
:init (global-undo-tree-mode)
:custom
(undo-tree-visualizer-timestamps t)
(undo-tree-visualizer-diff t)
(undo-tree-history-directory-alist '(("." . "~/.config/emacs/undo"))))
An autonomous emacs major-mode for editing web templates.
(use-package web-mode
:delight "☸ "
:hook ((css-mode web-mode) . rainbow-mode)
:mode (("\\.blade\\.php\\'" . web-mode)
("\\.html?\\'" . web-mode)
("\\.jsx\\'" . web-mode)
("\\.php$" . my/php-setup))
: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)))))
: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))
(add-hook 'web-mode-hook #'(lambda ()
(enable-minor-mode
'("\\.js?\\'" . prettier-js-mode))))
(add-hook 'web-mode-hook #'(lambda ()
(enable-minor-mode
'("\\.jsx?\\'" . prettier-js-mode))))
(add-hook 'web-mode-hook #'(lambda ()
(enable-minor-mode
'("\\.ts?\\'" . prettier-js-mode))))
(setq web-mode-code-indent-offset 2
web-mode-markup-indent-offset 2
web-mode-css-indent-offset 2
web-mode-enable-html-entities-fontification nil
web-mode-enable-block-face nil
web-mode-enable-comment-annotation nil
web-mode-enable-comment-interpolation nil
web-mode-enable-control-block-indentation nil
web-mode-enable-css-colorization nil
web-mode-enable-current-column-highlight nil
web-mode-enable-current-element-highlight nil
web-mode-enable-element-content-fontification nil
web-mode-enable-heredoc-fontification nil
web-mode-enable-inlays nil
web-mode-enable-optional-tags nil
web-mode-enable-part-face nil
web-mode-enable-sexp-functions nil
web-mode-enable-sql-detection nil
web-mode-enable-string-interpolation nil
web-mode-enable-whitespace-fontification nil
web-mode-enable-auto-expanding nil
web-mode-enable-auto-indentation nil
web-mode-enable-auto-closing nil
web-mode-enable-auto-opening nil
web-mode-enable-auto-pairing nil
web-mode-enable-auto-quoting nil)
It’s difficult to remember all the 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
:config (which-key-mode))
It is impossible to know everything, which is why a quick description of a term, without breaking its workflow, is ideal.
(use-package wiki-summary
:defer 1
: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))))
(advice-add 'wiki-summary/format-summary-in-buffer :override #'my/format-summary-in-buffer)
Highlight the indentation is a feature that visually pleases me. Indeed, without having to count the spaces, I can see that the code is well indented.
(use-package highlight-indent-guides
:defer 0.3
:hook (prog-mode . highlight-indent-guides-mode)
:custom (highlight-indent-guides-method 'character))
Provides the ability to have commands and their history saved so that whenever you return to work, you can re-run things as you need them. This is not a radical function, it is part of a good user experience.
(use-package savehist
:ensure nil
:custom
(history-delete-duplicates t)
(history-length t)
(savehist-additional-variables '(kill-ring search-ring regexp-search-ring))
(savehist-file (expand-file-name (format "%s/emacs/history" xdg-cache)))
(savehist-save-minibuffer-history 1)
:config (savehist-mode 1))
Hydra allows me to display a list of all the commands implemented in the echo area and easily interact with them.
Once you summon the Hydra through the prefixed binding (the body + any one head), all heads can be called in succession with only a short extension.
The Hydra is vanquished once Hercules, any binding that isn’t the Hydra’s head, arrives. Note that Hercules, besides vanquishing the Hydra, will still serve his original purpose, calling his proper command. This makes the Hydra very seamless, it’s like a minor mode that disables itself auto-magically.
(use-package hydra
:bind (("C-c I" . hydra-image/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 D" . dap-hydra)
("C-c e" . hydra-erc/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 q" . hydra-query/body)
("C-c s" . hydra-spelling/body)
("C-c t" . hydra-pytest/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)
"Displays an icon from all-the-icon."
(s-concat (all-the-icons-alltheicon icon :v-adjust (or v-adjust 0) :height (or height 1)) " " str))
(defun with-faicon (icon str &optional height v-adjust)
"Displays an icon from Font Awesome icon."
(s-concat (all-the-icons-faicon icon :v-adjust (or v-adjust 0) :height (or height 1)) " " str))
(defun with-fileicon (icon str &optional height v-adjust)
"Displays 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)) " " str))
(defun with-octicon (icon str &optional height v-adjust)
"Displays an icon from the GitHub Octicons."
(s-concat (all-the-icons-octicon icon :v-adjust (or v-adjust 0) :height (or height 1)) " " 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 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 ERC commands.
(pretty-hydra-define hydra-erc
(:hint nil :color teal :quit-key "q" :title (with-faicon "comments-o" "ERC" 1 -0.05))
("Action"
(("b" my/erc-browse-last-url "browse last url")
("c" my/erc-start-or-switch "connect")
("d" erc-quit-server "disconnect")
("j" erc-join-channel "join")
("n" erc-channel-names "names")
("o" my/erc-get-ops "ops")
("u" my/erc-count-users "users")
("r" my/erc-reset-track-mode "reset track mode"))))
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-faicon "file-text-o" "Go To" 1 -0.05))
("Agenda"
(("ac" (find-file "~/Documents/org/agenda/contacts.org") "contacts")
("ao" (find-file "~/Documents/org/agenda/organizer.org") "organizer")
("ap" (find-file "~/Documents/org/agenda/people.org") "people")
("ar" (find-file "~/Documents/org/agenda/routine.org") "routine")
("aw" (find-file "~/Documents/org/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")
("cd" (find-file (format "%s/dunst/dunstrc" xdg-config)))
("ce" (find-file (format "%s/emacs/config.org" xdg-config)) "emacs")
("cE" (find-file (format "%s/sh/environ" xdg-config)) "environ")
("ci" (find-file (format "%s/i3/config" xdg-config)) "i3")
("cn" (find-file (format "%s/neofetch/config.conf" xdg-config)) "neofetch")
("cs" (find-file (format "%s/sway/config" xdg-config)) "sway")
("ct" (find-file (format "%s/tmux/tmux.conf" xdg-config)) "tmux")
("cp" (find-file (format "%s/polybar/simple-black" xdg-config)) "polybar")
("cr" (find-file (format "%s/rofi/config.rasi" xdg-config)) "rofi")
("cx" (find-file (format "%s/sh/xdg" xdg-config)) "xdg"))
"Other"
(("ob" (find-file "~/Documents/org/other/books.org") "book")
("ol" (find-file "~/Documents/org/other/learning.org") "learning")
("om" (find-file "~/Documents/org/other/movies.org"))
("op" (find-file "~/Documents/org/other/purchases.org") "purchase")
("ou" (find-file "~/Documents/org/other/usb.org") "usb"))))
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 Magit commands.
(pretty-hydra-define hydra-magit
(:hint nil :color teal :quit-key "q" :title (with-alltheicon "git" "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-alltheicon "git" "Merge" 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")
("p" my/org-refile-to-blog-post-dir "blog-post")
("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" counsel-projectile-switch-to-buffer "list")
("k" projectile-kill-buffers "kill all")
("S" projectile-save-project-buffers "save all"))
"Find"
(("d" counsel-projectile-find-dir "directory")
("D" projectile-dired "root")
("f" counsel-projectile-find-file "file")
("p" counsel-projectile-switch-project "project"))
"Other"
(("c" projectile-compile-project "compile project")
("i" projectile-invalidate-cache "reset cache")
("x" projectile-run-project "run project"))
"Search"
(("r" projectile-replace "replace")
("R" projectile-replace-regexp "regexp replace")
("s" counsel-rg "search"))))
Group Query commands.
(pretty-hydra-define hydra-query
(:hint nil :color teal :quit-key "q" :title (with-faicon "search" "Engine-Mode" 1 -0.05))
("Query"
(("a" engine/search-amazon "amazon")
("d" engine/search-duckduckgo "duckduckgo")
("g" engine/search-github "github")
("i" engine/search-google-images "google images")
("m" engine/search-google-maps "google maps")
("s" engine/search-stack-overflow "stack overflow")
("w" engine/search-wikipedia "wikipedia")
("y" engine/search-youtube "youtube"))))
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 python-pytest commands.
(pretty-hydra-define hydra-pytest
(:hint nil :color teal :quit-key "q" :title (with-alltheicon "python" "Pytest" 1 -0.05))
("Action"
(("f" python-pytest-function-dwim "test function")
("a" python-pytest-file-dwim "test file")
("p" python-pytest "test project")
("m" python-pytest-dispatch "show menu"))))
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 contents"))))
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-buffe "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")
("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
, switch-to-buffer
, counsel-find-file
and many other functions; all-the-icons is just the best package that you can
find.
NOTE: If it’s the first time that you install the package, you must run
M-x all-the-icons-install-fonts
.
(use-package all-the-icons
:if (display-graphic-p)
:config (unless (find-font (font-spec :name "all-the-icons"))
(all-the-icons-install-fonts t)))
IRC is the best way for me to get a quick answer to a simple question and to
learn from more competent people than me on a subject. I’d rather use erc
than
rcirc
because I find rcirc
very minimal.
Besides, for people like me, who want to store your password in a GPG file,
you just need to specify a file priority list with auth-sources
, to tell erc
where to start looking for your password first.
Of course, don’t forget to add this line in your .authinfo.gpg
file, where
<nickname> and <password> match your login details.
machine irc.freenode.net login <nickname> password <password>
Then encrypt that file with gpg -c .authinfo
and don’t forget to delete the
.authinfo
file.
Finally, specify to erc
that you use a .authinfo
file with:
(setq erc-prompt-for-nickserv-password nil)
.
(use-package erc
:delight "ε "
:preface
(defun my/erc-browse-last-url ()
"Searchs backwards through an ERC buffer, looking for a URL. When a URL is
found, it prompts you to open it."
(interactive)
(save-excursion
(let ((ffap-url-regexp "\\(https?://\\)."))
(ffap-next-url t t))))
(defun my/erc-count-users ()
"Displays the number of users and ops connected on the current channel."
(interactive)
(if (get-buffer "irc.freenode.net:6667")
(let ((channel (erc-default-target)))
(if (and channel (erc-channel-p channel))
(let ((hash-table (with-current-buffer (erc-server-buffer)
erc-server-users))
(users 0)
(ops 0))
(maphash (lambda (k v)
(when (member (current-buffer)
(erc-server-user-buffers v))
(cl-incf users))
(when (erc-channel-user-op-p k)
(cl-incf ops)))
hash-table)
(message "%d users (%s ops) are online on %s" users ops channel))
(user-error "The current buffer is not a channel")))
(user-error "You must first be connected on IRC")))
(defun my/erc-get-ops ()
"Displays the names of ops users on the current channel."
(interactive)
(if (get-buffer "irc.freenode.net:6667")
(let ((channel (erc-default-target)))
(if (and channel (erc-channel-p channel))
(let (ops)
(maphash (lambda (nick cdata)
(if (and (cdr cdata)
(erc-channel-user-op (cdr cdata)))
(setq ops (cons nick ops))))
erc-channel-users)
(if ops
(message "The online ops users are: %s" (mapconcat 'identity ops " "))
(message "There are no ops users online on %s" channel)))
(user-error "The current buffer is not a channel")))
(user-error "You must first be connected on IRC")))
(defun my/erc-notify (nickname message)
"Displays a notification message for ERC."
(let* ((channel (buffer-name))
(nick (erc-hl-nicks-trim-irc-nick nickname))
(title (if (string-match-p (concat "^" nickname) channel)
nick
(concat nick " (" channel ")")))
(msg (s-trim (s-collapse-whitespace message))))
(alert (concat nick ": " msg) :title title)))
(defun my/erc-preprocess (string)
"Avoids channel flooding."
(setq str (string-trim (replace-regexp-in-string "\n+" " " str))))
(defun my/erc-reset-track-mode ()
"Resets ERC track mode."
(interactive)
(setq erc-modified-channels-alist nil)
(erc-modified-channels-update)
(erc-modified-channels-display)
(force-mode-line-update))
(defun my/erc-start-or-switch ()
"Connects to ERC, or switch to last active buffer."
(interactive)
(if (get-buffer "irc.freenode.net:6667")
(erc-track-switch-buffer 1)
(erc :server "irc.freenode.net" :port 6667 :nick "omgitsaheadcrab")))
:hook ((ercn-notify . my/erc-notify)
(erc-send-pre . my/erc-preprocess))
:custom-face
(erc-action-face ((t (:foreground "#8fbcbb"))))
(erc-error-face ((t (:foreground "#bf616a"))))
(erc-input-face ((t (:foreground "#ebcb8b"))))
(erc-notice-face ((t (:foreground "#ebcb8b"))))
(erc-timestamp-face ((t (:foreground "#a3be8c"))))
:custom
(erc-autojoin-channels-alist '(("freenode.net" "#archlinux" "#bash" "##c"
"##c++" "#emacs""#i3" "#linux" "#notmuch"
"#python")))
(erc-autojoin-timing 'ident)
(erc-fill-function 'erc-fill-static)
(erc-fill-static-center 22)
(erc-header-line-format "%n on %t (%m)")
(erc-hide-list '("JOIN" "PART" "QUIT"))
(erc-join-buffer 'bury)
(erc-kill-buffer-on-part t)
(erc-kill-queries-on-quit t)
(erc-use-auth-source-for-nickserv-password t)
(erc-kill-server-buffer-on-quit t)
(erc-lurker-hide-list '("JOIN" "PART" "QUIT"))
(erc-lurker-threshold-time 43200)
(erc-prompt-for-nickserv-password nil)
(erc-server-reconnect-attempts 5)
(erc-server-reconnect-timeout 3)
(erc-track-exclude-types '("JOIN" "MODE" "NICK" "PART" "QUIT"
"324" "329" "332" "333" "353" "477"))
:config
(add-to-list 'erc-modules 'notifications)
(add-to-list 'erc-modules 'spelling)
(erc-services-mode 1)
(erc-update-modules))
(use-package erc-hl-nicks :after erc)
(use-package erc-image :after erc)
I used helm
before, but I find ivy
faster and lighter.
Ivy is a generic completion mechanism for Emacs. While it operates similarly to other completion schemes such as icomplete-mode, Ivy aims to be more efficient, smaller, simpler, and smoother to use yet highly customizable.
(use-package counsel
:after ivy
:delight
:bind (("C-x C-d" . counsel-dired-jump)
("C-x C-h" . counsel-minibuffer-history)
("C-x C-l" . counsel-find-library)
("C-x C-r" . counsel-recentf)
("C-x C-u" . counsel-unicode-char)
("C-x C-v" . counsel-set-variable))
:config (counsel-mode)
:custom
(counsel-rg-base-command "rg -S -M 150 --no-heading --line-number --color never %s"))
(use-package ivy
:delight
:after ivy-rich
:bind (("C-x b" . ivy-switch-buffer)
("C-x B" . ivy-switch-buffer-other-window)
("M-H" . ivy-resume)
:map ivy-minibuffer-map
("<tab>" . ivy-alt-done)
("C-i" . ivy-partial-or-done)
("S-SPC" . nil)
:map ivy-switch-buffer-map
("C-k" . ivy-switch-buffer-kill))
:custom
(ivy-case-fold-search-default t)
(ivy-count-format "(%d/%d) ")
(ivy-re-builders-alist '((t . ivy--regex-plus)))
(ivy-use-virtual-buffers t)
:config (ivy-mode))
(global-set-key (kbd "C-x j") 'counsel-git-grep)
(setq counsel-find-file-ignore-regexp (regexp-opt '("__pycache__" ".pyc" ".pytest_cache")))
(use-package ivy-pass
:after ivy
:commands ivy-pass)
(use-package ivy-rich
:defer 0.1
:preface
(defun ivy-rich-branch-candidate (candidate)
"Displays the branch candidate of the candidate for ivy-rich."
(let ((candidate (expand-file-name candidate ivy--directory)))
(if (or (not (file-exists-p candidate)) (file-remote-p candidate))
""
(format "%s%s"
(propertize
(replace-regexp-in-string abbreviated-home-dir "~/"
(file-name-directory
(directory-file-name candidate)))
'face 'font-lock-doc-face)
(propertize
(file-name-nondirectory
(directory-file-name candidate))
'face 'success)))))
(defun ivy-rich-compiling (candidate)
"Displays compiling buffers of the candidate for ivy-rich."
(let* ((candidate (expand-file-name candidate ivy--directory)))
(if (or (not (file-exists-p candidate)) (file-remote-p candidate)
(not (magit-git-repo-p candidate)))
""
(if (my/projectile-compilation-buffers candidate)
"compiling"
""))))
(defun ivy-rich-file-group (candidate)
"Displays the file group of the candidate for ivy-rich"
(let ((candidate (expand-file-name candidate ivy--directory)))
(if (or (not (file-exists-p candidate)) (file-remote-p candidate))
""
(let* ((group-id (file-attribute-group-id (file-attributes candidate)))
(group-function (if (fboundp #'group-name) #'group-name #'identity))
(group-name (funcall group-function group-id)))
(format "%s" group-name)))))
(defun ivy-rich-file-modes (candidate)
"Displays the file mode of the candidate for ivy-rich."
(let ((candidate (expand-file-name candidate ivy--directory)))
(if (or (not (file-exists-p candidate)) (file-remote-p candidate))
""
(format "%s" (file-attribute-modes (file-attributes candidate))))))
(defun ivy-rich-file-size (candidate)
"Displays the file size of the candidate for ivy-rich."
(let ((candidate (expand-file-name candidate ivy--directory)))
(if (or (not (file-exists-p candidate)) (file-remote-p candidate))
""
(let ((size (file-attribute-size (file-attributes candidate))))
(cond
((> size 1000000) (format "%.1fM " (/ size 1000000.0)))
((> size 1000) (format "%.1fk " (/ size 1000.0)))
(t (format "%d " size)))))))
(defun ivy-rich-file-user (candidate)
"Displays the file user of the candidate for ivy-rich."
(let ((candidate (expand-file-name candidate ivy--directory)))
(if (or (not (file-exists-p candidate)) (file-remote-p candidate))
""
(let* ((user-id (file-attribute-user-id (file-attributes candidate)))
(user-name (user-login-name user-id)))
(format "%s" user-name)))))
(defun ivy-rich-switch-buffer-icon (candidate)
"Returns an icon for the candidate out of `all-the-icons'."
(with-current-buffer
(get-buffer candidate)
(let ((icon (all-the-icons-icon-for-mode major-mode :height 0.9)))
(if (symbolp icon)
(all-the-icons-icon-for-mode 'fundamental-mode :height 0.9)
icon))))
:config
(plist-put ivy-rich-display-transformers-list
'counsel-find-file
'(:columns
((ivy-rich-candidate (:width 73))
(ivy-rich-file-user (:width 8 :face font-lock-doc-face))
(ivy-rich-file-group (:width 4 :face font-lock-doc-face))
(ivy-rich-file-modes (:width 11 :face font-lock-doc-face))
(ivy-rich-file-size (:width 7 :face font-lock-doc-face))
(ivy-rich-file-last-modified-time (:width 30 :face font-lock-doc-face)))))
(plist-put ivy-rich-display-transformers-list
'counsel-projectile-switch-project
'(:columns
((ivy-rich-branch-candidate (:width 80))
(ivy-rich-compiling))))
(plist-put ivy-rich-display-transformers-list
'ivy-switch-buffer
'(:columns
((ivy-rich-switch-buffer-icon (:width 2))
(ivy-rich-candidate (:width 40))
(ivy-rich-switch-buffer-size (:width 7))
(ivy-rich-switch-buffer-indicators (:width 4 :face error :align right))
(ivy-rich-switch-buffer-major-mode (:width 20 :face warning)))
:predicate (lambda (cand) (get-buffer cand))))
(ivy-rich-mode 1)
(setq projectile-compile-project-cmd "cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=YES"))
(use-package all-the-icons-ivy
:after (all-the-icons ivy)
:custom (all-the-icons-ivy-buffer-commands '(ivy-switch-buffer-other-window))
:config
(add-to-list 'all-the-icons-ivy-file-commands 'counsel-dired-jump)
(add-to-list 'all-the-icons-ivy-file-commands 'counsel-find-library)
(all-the-icons-ivy-setup))
(use-package swiper
:after ivy
:bind (("C-s" . swiper)
:map swiper-map
("M-%" . swiper-query-replace)))
Setting buffer refresh to F5 as usual in other programs.
(global-set-key [f5]
#'(lambda () "Refresh the buffer from the disk (prompt of modified)."
(interactive)
(revert-buffer t (not (buffer-modified-p)) t)))
(global-set-key (kbd "C-+") 'text-scale-increase)
(global-set-key (kbd "C--") 'text-scale-decrease)
Rebind goto-line to Meta+g rather than Meta+g+g
(global-set-key "\M-g" 'goto-line)
Check if HHKB connected by counting occurences in dmesg output. If exists swap super and meta keys.
(defun count-occurences (regex string)
(recursive-count regex string 0))
(defun recursive-count (regex string start)
(if (string-match regex string start)
(+ 1 (recursive-count regex string (match-end 0)))
0))
(setq dmesg-out
(shell-command-to-string "/usr/bin/dmesg"))
(setq hhkb-times
(count-occurences "HHKB" dmesg-out))
(if (> hhkb-times 0)
(progn
;; (setq x-meta-keysym 'meta
;; x-super-keysym 'super)
(message "HHKB connected on Linux.")))
Check if host system is running MacOS and bind super and meta if true.
(if (string-equal system-type "darwin")
(progn
(setq mac-command-modifier 'meta
mac-option-modifier 'super)
(message "MacOS detected.")))
(defun reload-init-file ()
(interactive)
(load-file "~/.config/emacs/init.el"))
(global-set-key (kbd "C-s-M-u") 'reload-init-file)
Flycheck lints warnings and errors directly within buffers.
(use-package flycheck
:defer 2
:delight
:init (global-flycheck-mode)
:custom
(flycheck-display-errors-delay .3)
(flycheck-checker-error-threshold 1000)
(flycheck-pylintrc "~/.pylintrc")
(flycheck-python-pylint-executable "/usr/bin/pylint")
(flycheck-stylelintrc "~/.stylelintrc.json")
:config
(flycheck-add-mode 'javascript-eslint 'web-mode)
(flycheck-add-mode 'typescript-tslint 'web-mode))
I could use try
when I need to use lipsum
, but since I use defer
, the
packet load attribute has no impact on emacs-init-time
.
(use-package lorem-ipsum
:bind (("C-c C-v l" . lorem-ipsum-insert-list)
("C-c C-v p" . lorem-ipsum-insert-paragraphs)
("C-c C-v s" . lorem-ipsum-insert-sentences)))
Navigation is an important part of productivity. 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
).
FROM: http://emacsredux.com/blog/2013/05/22/smarter-navigation-to-the-beginning-of-a-line/
(defun my/smarter-move-beginning-of-line (arg)
"Moves 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))))
(global-set-key (kbd "C-a") 'my/smarter-move-beginning-of-line)
(use-package imenu
:ensure nil
:bind ("C-r" . imenu))
Using `notmuch` as my email client. I followed this guide.
To use you’ll need to install both
sudo pacman -S notmuch
yay -S gmailieer-git
(use-package notmuch
:ensure nil
:commands (notmuch)
:config
(add-hook 'notmuch-hello-mode-hook
(lambda () (display-line-numbers-mode 0))))
Configure your inboxes
(setq notmuch-saved-searches
'((:name "inbox (omgitsaheadcrab)" :query "tag:inbox AND tag:omgitsaheadcrab" :sort-order newest-first)
(:name "unread (omgitsaheadcrab)" :query "tag:unread AND tag:omgitsaheadcrab" :sort-order newest-first)
(:name "sent (omgitsaheadcrab)" :query "tag:sent AND tag:omgitsaheadcrab" :sort-order newest-first)
(:name "all mail (omgitsaheadcrab)" :query "tag:omgitsaheadcrab" :sort-order newest-first)
(:name "inbox (t.backerdirks)" :query "tag:inbox AND tag:t.backerdirks" :sort-order newest-first)
(:name "unread (t.backerdirks)" :query "tag:unread AND tag:t.backerdirks" :sort-order newest-first)
(:name "sent (t.backerdirks)" :query "tag:sent AND tag:t.backerdirks" :sort-order newest-first)
(:name "all mail (t.backerdirks)" :query "tag:t.backerdirks" :sort-order newest-first)
(:name "drafts" :query "tag:draft" :sort-order newest-first)))
Configure senders to automatically choose the correct auth from `.authinfo.gpg`
(setq mail-specify-envelope-from t)
(setq message-sendmail-envelope-from 'header)
(setq mail-envelope-from 'header)
(setq notmuch-always-prompt-for-sender t)
;; Configure known SMTP servers. Emacs prompts for passwords and saves them in ~/.authinfo
(setq smtp-accounts ;; Format: Sender Mail address - SMTP Server - Port - Username
'(("omgitsaheadcrab@gmail.com" "smtp.gmail.com" 587 "omgitsaheadcrab@gmail.com")
("t.backerdirks@gmail.com" "smtp.gmail.com" 587 "t.backerdirks@gmail.com")
))
;; Set the SMTP Server according to the mail address we use for sending
(defun my/set-smtp-server-message-send-and-exit ()
"Set SMTP server from list of multiple ones and send mail."
(interactive)
(message-remove-header "X-Message-SMTP-Method") ;; Remove. We always determine it by the From field
(let ((sender
(message-fetch-field "From")))
(cl-loop for (addr server port usr) in smtp-accounts
when (string-match addr sender)
do (message-add-header (format "X-Message-SMTP-Method: smtp %s %d %s" server port usr)))
(let ((xmess
(message-fetch-field "X-Message-SMTP-Method")))
(if xmess
(progn
(message (format "Sending message using '%s' with config '%s'" sender xmess))
(message-send-and-exit))
(error "Could not find SMTP Server for this Sender address: %s. You might want to correct it or add it to the SMTP Server list 'smtp-accounts'" sender)))))
;; Send emails via multiple servers
(defun my/local-notmuch-compose-mode ()
"Keys."
(local-set-key (kbd "C-c C-c") 'my/set-smtp-server-message-send-and-exit))
;; set in group mode hook
(add-hook 'notmuch-message-mode-hook 'my/local-notmuch-compose-mode)
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 "#262b36")
(set-face-bold 'show-paren-match t)
(set-face-foreground 'show-paren-match "#ffffff"))
rainbow-delimiters is a “rainbow parentheses”-like mode which highlights delimiters such as parentheses, brackets or braces according to their depth. Each successive level is highlighted in a different color. This makes it easy to spot matching delimiters, orient yourself in the code, and tell which statements are at a given depth.
(use-package rainbow-delimiters
:hook (prog-mode . rainbow-delimiters-mode))
In my opinion, it is the most powerful package to deal with the
parenthesis. Anyway, if you don’t like it, you can try taking a look at
paredit
or autopair
.
(use-package smartparens
:defer 1
:delight
:config
(setq sp-show-pair-from-inside nil)
(require 'smartparens-config)
(smartparens-global-mode t)
:diminish smartparens-mode)
This mode allows to paste whole buffers or parts of buffers to pastebin-like services. It supports more than one service and will failover if one service fails.
(use-package webpaste :defer 1)
Same principle for images with imgbb
. This package selects an image
and upload it to imgbb, making sure to display the URL of the image in
the minibuffer and place it in the kill ring.
(use-package imgbb :defer 2)
PDF Tools is, among other things, a replacement of DocView for PDF files. The key difference is that pages are not pre-rendered by e.g. ghostscript and stored in the file-system, but rather created on-demand and stored in memory.
(use-package pdf-tools
:defer 1
:magic ("%PDF" . pdf-view-mode)
:init (pdf-tools-install :no-query))
(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)
(pdf-view-use-unicode-ligther nil))
Increase region by semantic units. It tries to be smart about it and adapt to the structure of the current major mode.
(use-package expand-region
:bind (("C->" . er/contract-region)
("C-." . er/expand-region)))
I find it useful to delete a line and a region with only C-w
.
(defadvice kill-region (before slick-cut activate compile)
"When called interactively with no active region, kill a single line instead."
(interactive
(if mark-active (list (region-beginning) (region-end))
(list (line-beginning-position)
(line-beginning-position 2)))))
Projectile is a project interaction library for Emacs. Its goal is to provide a nice set of features operating on a project level without introducing external dependencies (when feasible). For instance - finding project files has a portable implementation written in pure Emacs Lisp without the use of GNU find (but for performance sake an indexing mechanism backed by external commands exists as well).
(use-package projectile
:defer 1
:preface
(defun my/projectile-compilation-buffers (&optional project)
"Get a list of a project's compilation buffers.
If PROJECT is not specified the command acts on the current project."
(let* ((project-root (or project (projectile-project-root)))
(buffer-list (mapcar #'process-buffer compilation-in-progress))
(all-buffers (cl-remove-if-not
(lambda (buffer)
(projectile-project-buffer-p buffer project-root))
buffer-list)))
(if projectile-buffers-filter-function
(funcall projectile-buffers-filter-function all-buffers)
all-buffers)))
:custom
(projectile-cache-file (expand-file-name (format "%s/emacs/projectile.cache" xdg-cache)))
(projectile-completion-system 'ivy)
(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)))
:config (projectile-global-mode))
(use-package counsel-projectile
:after (counsel projectile)
:config (counsel-projectile-mode 1))
Provides fast access to the recent files.
(use-package recentf
:bind ("C-c r" . recentf-open-files)
:init (recentf-mode)
:custom
(recentf-exclude (list "COMMIT_EDITMSG"
"~$"
"/scp:"
"/ssh:"
"/sudo:"
"/tmp/"))
(recentf-max-menu-items 15)
(recentf-max-saved-items 200)
(recentf-save-file (expand-file-name (format "%s/emacs/recentf" xdg-cache)))
:config (run-at-time nil (* 5 60) 'recentf-save-list))
Let’s follow the XDG base directory specification for the request and cookies
files, in order to make ~/.emacs.d
a cleaner place.
(use-package request
:ensure nil
:custom
(request-storage-directory (expand-file-name (format "%s/emacs/request/" xdg-data))))
(use-package url-cookie
:ensure nil
:custom
(url-cookie-file (expand-file-name (format "%s/emacs/url/cookies/" xdg-data))))
Smart M-x for recent and most used command history.
(use-package smex
:init (setq-default smex-history-length 10))
According to a list of misspelled words, abbrev
auto-correct 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)))
For the other words that would not be in my list of abbreviations, flyspell
enables spell checking on-the-fly in GNU Emacs.
(use-package flyspell
:delight
:hook ((markdown-mode org-mode text-mode) . flyspell-mode)
(prog-mode . flyspell-prog-mode)
:custom
(flyspell-abbrev-p t)
(flyspell-default-dictionary "en_US")
(flyspell-issue-message-flag nil)
(flyspell-issue-welcome-flag nil))
(use-package flyspell-correct-ivy
:after (flyspell ivy)
:init (setq flyspell-correct-interface #'flyspell-correct-ivy))
No one is immune to spelling mistakes. So I like to check the spelling of the
document once it has been written. To do this, I use hunspell
, the modern
spell checker.
NOTE: the reason I prefer hunspell
to aspell
is that according to the
latest news, hunspell has made it possible to be more consistent on fly
spells. However, most people still use aspell
because it allows you to spot
errors in camelCase, convenient for when you program. Personally, I just want to
check the spelling in the comments and not in the whole document, so hunspell
is
perfect for me.
To use hunspell
and the desired dictionaries on GNU Emacs, you must first
install them (e.g. hunspell-en_US
, hunspell-fr
) with the package manager of
your operating system.
(use-package ispell
:defer 2
:ensure-system-package (hunspell . "yay -S hunspell")
:custom
;; to remove
(ispell-local-dictionary "en_US")
(ispell-local-dictionary-alist
'(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)
("fr_BE" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "fr_BE") nil utf-8)))
(ispell-dictionary "en_US")
(ispell-dictionary-alist
'(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)
("fr_BE" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "fr_BE") nil utf-8)))
(ispell-program-name (executable-find "hunspell"))
(ispell-really-hunspell t)
(ispell-silently-savep t)
:preface
(defun my/switch-language ()
"Switches between the English and French language."
(interactive)
(let* ((current-dictionary ispell-current-dictionary)
(new-dictionary (if (string= current-dictionary "fr_BE") "en_US" "fr_BE")))
(ispell-change-dictionary new-dictionary)
(if (string= new-dictionary "fr_BE")
(langtool-switch-default-language "fr")
(langtool-switch-default-language "en"))
;;Clears all these old errors after switching to the new language
(if (and (boundp 'flyspell-mode) flyspell-mode)
(flyspell-mode 0)
(flyspell-mode 1))
(message "Dictionary switched from %s to %s" current-dictionary new-dictionary))))
LanguageTool is great for correcting your grammar. Combined with abbrev-mode
and flyspell
, you will have better documents. In order to be able to use it
locally, download the desktop version and change the paths indicated below.
(use-package langtool
:defer 2
:delight
:custom
(langtool-default-language "en")
(langtool-disabled-rules '("COMMA_PARENTHESIS_WHITESPACE"
"COPYRIGHT"
"DASH_RULE"
"EN_QUOTES"
"EN_UNPAIRED_BRACKETS"
"UPPERCASE_SENTENCE_START"
"WHITESPACE_RULE"))
(langtool-java-classpath
"/usr/share/languagetool:/usr/share/java/languagetool/*")
(langtool-mother-tongue "en"))
Enable more terminal colours.
(use-package eterm-256color
:config
(add-hook 'term-mode-hook #'eterm-256color-mode))
(use-package vterm
:config
(defun turn-off-chrome ()
(hl-line-mode -1)
(display-line-numbers-mode -1))
:hook (vterm-mode . turn-off-chrome))
Setting hotkey for vterm in emacs - Super+T.
(use-package vterm-toggle
:custom
(vterm-toggle-fullscreen-p nil "Open a vterm in another window.")
(vterm-toggle-scope 'project)
:bind ("s-t" . #'vterm-toggle))
Setting yes and no to y and n for brevity and consistency.
(fset 'yes-or-no-p 'y-or-n-p)
(delete-selection-mode 1)
(use-package iedit
:bind ("C-:" . iedit-mode))
;; M-↑,↓ moves line, or marked region; prefix is how many lines.
(use-package move-text
:config (move-text-default-bindings))
Persistent the scratch buffer
(use-package persistent-scratch
:preface
(defun my-save-buffer ()
"Save scratch and other buffer."
(interactive)
(let ((scratch-name "*scratch*"))
(if (string-equal (buffer-name) scratch-name)
(progn
(message "Saving %s..." scratch-name)
(persistent-scratch-save)
(message "Wrote %s" scratch-name))
(save-buffer))))
:hook (persistent-scratch-setup-default)
:bind (:map org-mode-map
("C-x C-s" . my-save-buffer))
:config (persistent-scratch-setup-default))
Set persistent scratch as an org-mode buffer
(setq initial-major-mode (lambda ()
(persistent-scratch-mode)
(org-mode)))
(defun quote-lines ()
"Change current text block's lines to quoted lines with comma or other separator char.
When there is a text selection, act on the selection, else, act on a text block separated by blank lines.
For example,
cat
dog
cow
becomes
\"cat\",
\"dog\",
\"cow\",
or
(cat)
(dog)
(cow)
If the delimiter is any left bracket, the end delimiter is automatically the matching bracket.
URL `http://ergoemacs.org/emacs/emacs_quote_lines.html'
Version 2017-01-08"
(interactive)
(let* (
$p1
$p2
($quoteToUse
(read-string
"Quote to use:" "\"" nil
'(
""
"\""
"'"
"("
"{"
"["
)))
($separator
(read-string
"line separator:" "," nil
'(
""
","
";"
)))
($beginQuote $quoteToUse)
($endQuote
;; if begin quote is a bracket, set end quote to the matching one. else, same as begin quote
(let (($syntableValue (aref (syntax-table) (string-to-char $beginQuote))))
(if (eq (car $syntableValue ) 4) ; ; syntax table, code 4 is open paren
(char-to-string (cdr $syntableValue))
$quoteToUse
))))
(if (use-region-p)
(progn
(setq $p1 (region-beginning))
(setq $p2 (region-end)))
(progn
(if (re-search-backward "\n[ \t]*\n" nil "NOERROR")
(progn (re-search-forward "\n[ \t]*\n")
(setq $p1 (point)))
(setq $p1 (point)))
(re-search-forward "\n[ \t]*\n" nil "NOERROR")
(skip-chars-backward " \t\n" )
(setq $p2 (point))))
(save-excursion
(save-restriction
(narrow-to-region $p1 $p2)
(goto-char (point-min))
(skip-chars-forward "\t ")
(insert $beginQuote)
(goto-char (point-max))
(insert $endQuote)
(goto-char (point-min))
(while (re-search-forward "\n\\([\t ]*\\)" nil "NOERROR" )
(replace-match
(concat $endQuote $separator (concat "\n" (match-string 1)) $beginQuote) "FIXEDCASE" "LITERAL"))
;;
))))
(use-package sudo-edit
:bind ("s-u" . sudo-edit))
(setq locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
(when (display-graphic-p)
(setq x-select-request-type '(UTF8_STRING COMPOUND_TEXT TEXT STRING)))
(define-coding-system-alias 'UTF-8 'utf-8)
Apparently there is a warning bell/beep and light - not seen it though..
(setq ring-bell-function 'ignore)
It is quite common to work on Git repositories, so it is important to have a configuration that we like.
Magit is an interface to the version control system Git, implemented as an Emacs package. Magit aspires to be a complete Git porcelain. While we cannot (yet) claim that Magit wraps and improves upon each and every Git command, it is complete enough to allow even experienced Git users to perform almost all of their daily version control tasks directly from within Emacs. While many fine Git clients exist, only Magit and Git itself deserve to be called porcelains.
(use-package git-commit
:after magit
:hook (git-commit-mode . my/git-commit-auto-fill-everywhere)
:custom (git-commit-summary-max-length 50)
:preface
(defun my/git-commit-auto-fill-everywhere ()
"Ensures that the commit body does not exceed 72 characters."
(setq fill-column 72)
(setq-local comment-auto-fill-only-comments nil)))
(use-package magit :defer 0.3)
(use-package smerge-mode
:after hydra
:hook (magit-diff-visit-file . (lambda ()
(when smerge-mode
(hydra-merge/body)))))
Finally, one last package that I like to use with Git to easily see the changes made by previous commits.
(use-package git-timemachine
:defer 1
:delight)
A Static HTML Generator for Emacs and Org-Mode.
(use-package weblorg
:delight)
It is often annoying to see unnecessary blank spaces at the end of a line or file. Let’s get ride of them:
(use-package simple
:ensure nil
:hook (before-save . delete-trailing-whitespace))
Deleting a whitespace character will delete all whitespace until the next non-whitespace character.
(use-package hungry-delete
:defer 0.7
:delight
:config (global-hungry-delete-mode))
Don’t ask before killing a buffer. I know what I’m doing.
(global-set-key [remap kill-buffer] #'kill-this-buffer)
Most of the time, when I open a new window with C-x 2
or C-x 3
it is to
switch directly to it and perform an action. By default, GNU Emacs does not give
focus to the new window created. I have no idea why this is not the default
behavior. But let’s refine these keys:
(use-package window
:ensure nil
:bind (("C-x 3" . hsplit-last-buffer)
("C-x 2" . vsplit-last-buffer))
:preface
(defun hsplit-last-buffer ()
"Gives the focus to the last created horizontal window."
(interactive)
(split-window-horizontally)
(other-window 1))
(defun vsplit-last-buffer ()
"Gives the focus to the last created vertical window."
(interactive)
(split-window-vertically)
(other-window 1)))
Displays an overlay in each window showing a unique key, then asks the user where to move in the window.
Most people use ace-window
, but I prefer switch-window
because I find this
package more ergonomic by using the fact of displaying the buffer number by
hiding its contents.
(use-package switch-window
:bind (("C-x o" . switch-window)
("C-x w" . switch-window-then-swap-buffer)))
Allows you to move from one window to another with something more natural than
cycling through C-x o
(other-window
).
(use-package windmove
:bind (("C-c h" . windmove-left)
("C-c j" . windmove-down)
("C-c k" . windmove-up)
("C-c l" . windmove-right)))
I often undo’s and redo’s with window configurations.
Winner mode is a global minor mode that records the changes in the window configuration (i.e. how the frames are partitioned into windows) so that the changes can be “undone” using the command
winner-undo
. By default, this one is bound to the key sequence ctrl-c left. If you change your mind (while undoing), you can press ctrl-c right (callingwinner-redo
).
(use-package winner
:defer 2
:config (winner-mode 1))
I like to have lines of the same length in text mode.
(use-package simple
:ensure nil
:delight (auto-fill-function)
:bind ("C-x p" . pop-to-mark-command)
:hook (text-mode . turn-on-auto-fill)
:custom (set-mark-command-repeat-pop t))
YASnippet is a template system for Emacs. It allows you to type an abbreviation and automatically expand it into function templates.
(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)
Extend the deft package and turn it into a (very very) basic Zettelkasten note-taking system. Requires Avy for text movement.
(use-package avy)
(use-package zetteldeft)
(zetteldeft-set-classic-keybindings)
One of my favorite modes in GNU Emacs. I mainly use it to organize my life, take
notes and make my presentations, but you can do lots of things with
it. org-mode
it’s like the sky, without limits.
Org mode is for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system.
(use-package org
:ensure org-contrib
:delight "Θ "
:bind ("C-c i" . org-insert-structure-template)
:preface
(defvar my/org-default-blog-posts-dir "~/Documents/org/site/posts" "Default directory for blog posts")
(defun my/org-compare-times (clocked estimated)
"Gets the ratio between the timed time and the estimated time."
(if (and (> (length clocked) 0) estimated)
(format "%.2f"
(/ (* 1.0 (org-hh:mm-string-to-minutes clocked))
(org-hh:mm-string-to-minutes estimated)))
""))
(defvar my/delete-frame-after-capture 0 "Whether to delete the last frame after the current capture")
(defun my/delete-frame-if-neccessary (&rest r)
(cond
((= my/delete-frame-after-capture 0) nil)
(t
(setq my/delete-frame-after-capture 0)
(delete-frame))))
(defun my/org-archive-done-tasks ()
"Archives 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 ()
"Jumps to a specific task."
(interactive)
(let ((current-prefix-arg '(4)))
(call-interactively 'org-refile)))
(defun my/org-use-speed-commands-for-headings-and-lists ()
"Activates 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))
:custom
(org-archive-location "~/Documents/org/archives/%s::")
(org-blank-before-new-entry '((heading . t)
(plain-list-item . t)))
(org-cycle-include-plain-lists 'integrate)
(org-ditaa-jar-path "~/.local/lib/ditaa0_9.jar")
(org-expiry-inactive-timestamps t)
(org-export-backends '(ascii beamer html icalendar latex man md org texinfo))
(org-log-done 'time)
(org-log-into-drawer "LOGBOOK")
(org-modules '(org-crypt
org-habit
ol-info
ol-irc
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 . 6))))
(org-startup-folded nil)
(org-startup-with-inline-images t)
(org-tag-alist '(("@coding" . ?c)
("@computer" . ?l)
("@errands" . ?e)
("@home" . ?h)
("@phone" . ?p)
("@reading" . ?r)
("@school" . ?s)
("@work" . ?b)
("@writing" . ?w)
("crypt" . ?C)
("fuzzy" . ?0)
("highenergy" . ?1)))
(org-tags-exclude-from-inheritance '("crypt" "project"))
(org-todo-keywords '((sequence "TODO(t)"
"STARTED(s)"
"WAITING(w@/!)"
"SOMEDAY(.)" "|" "DONE(d!)" "CANCELLED(c@)")
(sequence "DONE(d)")))
(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 '("!" my/org-clock-in-and-track))
(add-to-list 'org-speed-commands '("$" call-interactively 'org-archive-subtree))
(add-to-list 'org-speed-commands '("d" my/org-move-line-to-destination))
(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"))
(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-todo :after (ignore-args #'org-save-all-org-buffers))
(advice-add 'org-capture-finalize :after 'my/delete-frame-if-neccessary)
(advice-add 'org-capture-kill :after 'my/delete-frame-if-neccessary)
(advice-add 'org-capture-refile :after 'my/delete-frame-if-neccessary)
(org-clock-persistence-insinuate)
(org-load-modules-maybe t))
If like me, you’re tired of manually updating your tables of contents, toc-org
will maintain a table of contents at the first heading that has a :TOC:
tag.
(use-package toc-org
:after org
:hook (org-mode . toc-org-enable))
For a cleaner online mode.
(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.
org-agenda
allows me to be organized with daily tasks. As a result, I can use
my time to the fullest.
I put my org
files in Syncthing in order to be able to check my agenda and
update it from several computers and smartphones.
(use-package org-agenda
:ensure nil
:bind (:map org-agenda-mode-map
("X" . my/org-agenda-mark-done-and-add-followup)
("x" . my/org-agenda-done))
:preface
(defun my/org-agenda-done (&optional arg)
"Mark current TODO as done.
This changes the line at point, all other lines in the agenda referring to
the same tree node, and the headline of the tree node in the Org-mode file."
(interactive "P")
(org-agenda-todo "DONE"))
(defun my/org-agenda-mark-done-and-add-followup ()
"Mark the current TODO as done and add another task after it.
Creates it at the same level as the previous task, so it's better to use
this with to-do items than with projects or headings."
(interactive)
(org-agenda-todo "DONE")
(org-agenda-switch-to)
(org-capture 0 "t"))
:custom
(org-directory "~/Documents/org")
(org-agenda-dim-blocked-tasks t)
(org-agenda-files (directory-files-recursively org-directory "\\.org$"))
(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-sticky nil)
(org-agenda-tags-column -100)
(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 (format "%s/%s" org-directory "inbox.org"))
(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))
Prettier bullets in org-mode.
(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
:after org
:preface
(defvar my/org-basic-task-template "* TODO %^{Task}
:PROPERTIES:
:Effort: %^{effort|1:00|0:05|0:15|0:30|2:00|4:00}
:END:
Captured %<%Y-%m-%d %H:%M>" "Template for basic task.")
(defvar my/org-contacts-template "* %(org-contacts-template-name)
:PROPERTIES:
:ADDRESS: %^{289 Cleveland St. Brooklyn, 11206 NY, USA}
:BIRTHDAY: %^{yyyy-mm-dd}
:EMAIL: %(org-contacts-template-email)
:NOTE: %^{NOTE}
:END:" "Template for org-contacts.")
(defvar my/org-blog-post-template "* TODO %^{Post Title}
#+DATE: <%<%Y-%m-%d>>
#+OPTIONS: toc:nil num:nil
#+FILETAGS: %^{:blogging:}" "Template for org-blog-posts.")
(defun transform-square-brackets-to-round-ones(string-to-transform)
"Transforms [ into ( and ] into ), other chars left unchanged."
(concat
(mapcar (lambda (c) (if (equal c ?\[) ?\( (if (equal c ?\]) ?\) c))) string-to-transform))
)
:custom
(org-capture-templates
`(("B" "Book" checkitem (file+headline "~/Documents/org/other/books.org" "Books")
"- [ ] %^{Book}"
:immediate-finish t)
("w" "Work" entry (file+headline "~/Documents/org/agenda/work.org" "Tasks"),
my/org-basic-task-template
:empty-lines 1)
("t" "Task" entry (file+headline "~/Documents/org/agenda/organizer.org" "Tasks"),
my/org-basic-task-template
:empty-lines 1)
("i" "Inbox" entry (file+headline "~/Documents/org/inbox.org" "Inbox"),
my/org-basic-task-template
:empty-lines 1)
("p" "Post" entry (file+headline "~/Documents/org/inbox.org" "Inbox"),
my/org-blog-post-template
:empty-lines 1)
("q" "Protocol Quote" entry (file+headline "~/Documents/org/inbox.org" "Inbox")
"* TODO %^{Title}\nSource: [[%:link][%(transform-square-brackets-to-round-ones \"%:description\")]]\n#+BEGIN_QUOTE\n%i\n#+END_QUOTE\n\n%? %(progn (setq my/delete-frame-after-capture 1) \"\")\nCaptured On: %U"
:empty-lines 1)
("l" "Protocol Link" entry (file+headline "~/Documents/org/inbox.org" "Inbox")
"* TODO %? [[%:link][%(transform-square-brackets-to-round-ones \"%:description\")]] %(progn (setq my/delete-frame-after-capture 1) \"\")\nCaptured On: %U"
:empty-lines 1)
)))
Being organized is one thing, but being optimal is another. org-clock
allows
you to estimate your tasks and time them. This is useful, since with experience,
you can have a better estimate of the time that needs to be given to each task.
(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))))
Being a person with a very low ability to concentrate, I use the pomodoro
method to be able to concentrate throughout the day and have deep focus sessions
almost all the time.
Personally, I do sessions of:
- 25 minutes of concentrated work;
- 5 minutes break.
Finally, I take a 30-minute break every two hours.
To allow this, I use org-pomodoro
making sure I get that in my status bar:
(use-package org-pomodoro
:defer 0.5
: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))
The best solution to maintain your contacts. I tend to use org-contacts
to
remember their birthdays, so I can be the first to wish them that. Be careful
that to install it, this one is available with org-contrib
.
(use-package org-contacts
:ensure nil
:after org
:custom (org-contacts-files '("~/Documents/org/agenda/contacts.org")))
Let’s change the foreground and the weight of each keyword.
(use-package org-faces
:ensure nil
:after org
:custom
(org-todo-keyword-faces
'(("DONE" . (:foreground "cyan" :weight bold))
("SOMEDAY" . (:foreground "gray" :weight bold))
("TODO" . (:foreground "green" :weight bold))
("WAITING" . (:foreground "red" :weight bold)))))
To be able to enable encryption and decryption of .gpg
files with org-mode
,
we will need to install gnupg2
.
Once this is done, we simply configure org-crypt
to accept our public key
identifier to allow asymmetric encryption.
NOTE: you need to modify the org-crypt-key
variable to replace my key
identifier, by yours (or nil
to allow symmetric encryption).
(use-package org-crypt
:ensure nil
:after org
:init (org-crypt-use-before-save-magic)
:custom (org-crypt-key "E9AADC36E94A672D1A07D49B208FCDBB98190562"))
Recently, I started writing a journal about my daily life as I read that journals improve mental clarity, help solve problems, improve overall focus, insight and understanding, track the overall development and facilitate personal growth.
(use-package org-journal
:after org
:bind (("C-c T" . org-journal-new-entry)
("C-c Y" . journal-file-yesterday))
:preface
(defun get-journal-file-yesterday ()
"Gets filename for yesterday's journal entry."
(let* ((yesterday (time-subtract (current-time) (days-to-time 1)))
(daily-name (format-time-string "%Y%m%d" yesterday)))
(expand-file-name (concat org-journal-dir daily-name))))
(defun journal-file-yesterday ()
"Creates and load a file based on yesterday's date."
(interactive)
(find-file (get-journal-file-yesterday)))
:custom
(org-journal-date-format "%e %b %Y (%A)")
(org-journal-dir (format "~/Documents/org/journal/" (format-time-string "%Y")))
(org-journal-enable-encryption t)
(org-journal-file-format "%Y%m%d")
(org-journal-time-format ""))
With that, I can compile many languages with org-mode
.
(use-package ob-C :ensure nil :after org)
(use-package ob-css :ensure nil :after org)
(use-package ob-ditaa :ensure nil :after org)
(use-package ob-dot :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-ruby :ensure nil :after org)
(use-package ob-shell :ensure nil :after org)
(use-package ob-sql :ensure nil :after org)
Add ability to refile org subtrees to a new file.
Custom functions to enable refiling org subtrees to a new file.
FROM: http://www.howardism.org/Technical/Emacs/getting-even-more-boxes-done.html
(defun my/org-subtree-metadata ()
"Return a list of key aspects of an org-subtree. Includes the
following: header text, body contents, list of tags, region list
of the start and end of the subtree."
(save-excursion
;; Jump to the parent header if not already on a header
(when (not (org-at-heading-p))
(org-previous-visible-heading 1))
(let* ((context (org-element-context))
(attrs (second context))
(props (org-entry-properties)))
(list :region (list (plist-get attrs :begin) (plist-get attrs :end))
:header (plist-get attrs :title)
:tags (my/org-get-subtree-tags props)
:properties (my/org-get-subtree-properties attrs)
:body (my/org-get-subtree-content attrs)))))
(defun my/org-get-subtree-tags (&optional props)
"Given the properties, PROPS, from a call to
`org-entry-properties', return a list of tags."
(unless props
(setq props (org-entry-properties)))
(let ((tag-label (if my/org-get-subtree-tags-inherited "ALLTAGS" "TAGS")))
(-some->> props
(assoc tag-label)
cdr
substring-no-properties
(s-split ":")
(--filter (not (equalp "" it))))))
(defvar my/org-get-subtree-tags-inherited t
"Returns a subtree's tags, and all tags inherited (from tags
specified in parents headlines or on the file itself). Defaults
to true.")
(defun my/org-get-subtree-properties (attributes)
"Return a list of tuples of a subtrees properties where the keys are strings."
(defun my/symbol-upcase? (sym)
(let ((case-fold-search nil))
(string-match-p "^:[A-Z]+$" (symbol-name sym))))
(defun my/convert-tuple (tup)
(let ((key (first tup))
(val (second tup)))
(list (substring (symbol-name key) 1) val)))
(->> attributes
(-partition 2) ; Convert plist to list of tuples
(--filter (my/symbol-upcase? (first it))) ; Remove lowercase tuples
(-map 'my/convert-tuple)))
(defun my/org-get-subtree-content (attributes)
"Return the contents of the current subtree as a string."
(let ((header-components '(clock diary-sexp drawer headline inlinetask
node-property planning property-drawer section)))
(goto-char (plist-get attributes :contents-begin))
;; Walk down past the properties, etc.
(while
(let* ((cntx (org-element-context))
(elem (first cntx))
(props (second cntx)))
(when (member elem header-components)
(goto-char (plist-get props :end)))))
;; At this point, we are at the beginning of what we consider
;; the contents of the subtree, so we can return part of the buffer:
(buffer-substring-no-properties (point) (org-end-of-subtree))))
(defun my/org-refile-subtree-to-file (dir)
"Archive the org-mode subtree and create an entry in the
directory folder specified by DIR. It attempts to move as many of
the subtree's properties and other features to the new file."
(interactive "DDestination: ")
(let* ((props (my/org-subtree-metadata))
(head (plist-get props :header))
(body (plist-get props :body))
(tags (plist-get props :tags))
(properties (plist-get props :properties))
(area (plist-get props :region))
(filename (my/org-filename-from-title head))
(filepath (format "%s/%s.org" dir filename)))
(apply #'delete-region area)
(my/org-create-org-file filepath head body tags properties)))
(defun my/org-create-org-file (filepath header body tags properties)
"Create a new Org file by FILEPATH. The contents of the file is
pre-populated with the HEADER, BODY and any associated TAGS."
(find-file-other-window filepath)
(my/org-set-file-property "TITLE" header t)
(when tags
(my/org-set-file-property "FILETAGS" (s-join " " tags)))
;; Insert any drawer properties as #+PROPERTY entries:
(when properties
(goto-char (point-min))
(or (re-search-forward "^\s*$" nil t) (point-max))
(--map (insert (format "#+PROPERTY: %s %s" (first it) (second it))) properties))
;; My auto-insert often adds an initial headline for a subtree, and in this
;; case, I don't want that... Yeah, this isn't really globally applicable,
;; but it shouldn't cause a problem for others.
(when (re-search-forward "^\\* [0-9]$" nil t)
(replace-match ""))
(delete-blank-lines)
(goto-char (point-max))
(insert "\n")
(insert body))
(defun my/org-filename-from-title (title)
"Creates a useful filename based on a header string, TITLE.
For instance, given the string: What's all this then?
This function will return: whats-all-this-then"
(let* ((no-letters (rx (one-or-more (not alphanumeric))))
(init-try (->> title
downcase
(replace-regexp-in-string "'" "")
(replace-regexp-in-string no-letters "-"))))
(string-trim init-try "-+" "-+")))
(defun my/org-set-file-property (key value &optional spot)
"Make sure file contains a top-level, file-wide property.
KEY is something like `TITLE' or `FILETAGS'. This function makes
sure that the property contains the contents of VALUE, and if the
file doesn't have the property, it is inserted at either SPOT, or
if nil,the top of the file."
(save-excursion
(goto-char (point-min))
(let ((case-fold-search t))
(if (re-search-forward (format "^#\\+%s:\s*\\(.*\\)" key) nil t)
(replace-match value nil nil nil 1)
(cond
;; if SPOT is a number, go to it:
((numberp spot) (goto-char spot))
;; If SPOT is not given, jump to first blank line:
((null spot) (progn (goto-char (point-min))
(re-search-forward "^\s*$" nil t)))
(t (goto-char (point-min))))
(insert (format "#+%s: %s\n" (upcase key) value))))))
(defun my/org-refile-to-blog-post-dir ()
"Move the current subtree to a file in the blog `posts' directory."
(interactive)
(my/org-refile-subtree-to-file my/org-default-blog-posts-dir))