My personal literate emacs configuration.
Notice that this file serves as my emacs configuration file, but also as a sort of repository for code I’ve collected. Hence, you’ll see many commented pieces of code. These are configurations which I choose not to use, but still wish to keep around.
Emacs has a ridiculously low garbage collection threshold.
The default value of gc-cons-threshold
is 800 000 bytes, which amounts to less than 1 MB.
While garbage collection is not automatically performed once the thresholds, given by gc-cons-percentage
or gc-cons-threshold
, are reached, it may happen at any moment.
Hence, it’s possible for garbage collection to be triggered several times in a short time.
This means constant waiting periods before garbage collection is finalised.
To fix these issues, increase the threshold and also force garbage collection when on idle.
;; set threshold for garbage collection at 100MB
(setq gc-cons-threshold (* 100 1024 1024))
;; trigger garbage collection 4 seconds after emacs goes idle
(run-with-idle-timer 4 t (lambda () (garbage-collect)))
The standard Emacs configuration clutters the filesystem with extra files, leaving backups, autosaves and lockfiles everywhere. I prefer to keep my filesystem more tidy, hence I neatly store backups and autosaves in a single location. I also disable lockfiles: there’s no need for them since there’s only one user at a time.
;; enable automatic backups
(setq make-backup-files t)
;; save backup files in a specific folder (created automatically) to avoid cluttering
(setq backup-directory-alist
`((".*" . ,(expand-file-name "emacs-backups/" temporary-file-directory))))
;; backup files by copying
;; (setq backup-by-copying t)
;; backup symlinks by copying
;; (setq backup-by-copying-when-linked t)
;; enable automatic autosaves
(setq auto-save-default t)
;; specify the regularity of autosaves
(setq auto-save-interval 20)
;; unless autosaves directory exists, create it
(unless (file-exists-p (expand-file-name "emacs-autosaves/" temporary-file-directory))
(make-directory (expand-file-name "emacs-autosaves/" temporary-file-directory)))
;; save autosave files in a specific folder, to avoid cluttering
(setq auto-save-file-name-transforms
`((".*" ,(expand-file-name "emacs-autosaves/" temporary-file-directory) t)))
;; do not lock files
(setq create-lockfiles nil)
Set global configuration variables:
;; display both current line and collumn numbers for pointer
(setq column-number-mode t)
(setq line-number-mode t)
;; display box cursor
(setq-default cursor-type 'box)
;; ;; display line numbers relative to current cursor line
;; (setq display-line-numbers-type 'relative)
;; use spaces for indentation instead of tabs
(setq-default indent-tabs-mode nil)
;; ;; Do not show the startup screen.
;; (setq inhibit-startup-message t)
;; kill the whole line, including newline, if at collumn zero
;; kill up to newline if not at collumn zero
(setq kill-whole-line t)
;; Save existing clipboard text into the kill ring before replacing it.
(setq save-interprogram-paste-before-kill t)
;; do no save duplicate entries into kill ring
(setq kill-do-not-save-duplicates t)
;; default tab width set to 2 spaces
(setq-default tab-width 2)
;; wrap text at words (doesn't treat as having a newline at the end)
(setq-default word-wrap t)
;; place custom-set-variables and custom-set-faces into their own file
(setq custom-file (concat user-emacs-directory "custom.el"))
(when (file-exists-p custom-file)
(load custom-file))
;; show hyperlinks as the underlying plain text
(setq org-descriptive-links nil)
Enable / disable modes:
;; ;; save previous emacs session
;; (desktop-save-mode 1)
;; disable electric-indent-mode
(electric-indent-mode -1)
;; update buffer when file changes on disk
(global-auto-revert-mode 1)
;; display line numbers
(global-display-line-numbers-mode t)
;; highlight current line
(global-hl-line-mode t)
;; ;; wrap text at words (treats as having a newline at the end)
;; (global-visual-line-mode t)
;; do not display menu bar, tool bar and scroll bar
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
;; remember and restore the last cursor location of opened files
(save-place-mode 1)
;; display matching parenthesis
(show-paren-mode 1)
Customise key bindings:
;; unset key bind to suspend emacs
(when (display-graphic-p)
(global-unset-key (kbd "C-z"))
(global-unset-key (kbd "C-x C-z")))
Custom functions:
(defun file-to-string (file)
"Read file contents and return as string"
(with-temp-buffer
(insert-file-contents file)
(buffer-string)))
(defun string-reset-width (string)
"Remove all newline characters from string, effectively adjusting the width to
size of string"
(replace-regexp-in-string "\n" "" string))
(defun string-nth (n string)
"Obtain char at the nth position in the string"
(substring string n (+ n 1)))
;; (defun split-string-max-length (string max-length)
;; "Split a string into substrings with a max length of max-length"
;; (let ((new-string nil)
;; (rest-string string))
;; (let ((number-substrings (if (= 0 (mod (length string) max-length))
;; (/ (length string) max-length)
;; (+ 1 (/ (length string) max-length)))))
;; (progn
;; (dotimes (number number-substrings)
;; (progn
;; (setq new-string (concat new-string
;; (seq-take rest-string max-length) "\n"))
;; (setq rest-string (seq-drop rest-string max-length))))
;; new-string))))
(defun string-adjust-width (string width)
"Adjust string width to width by moving newlines"
(let* ((clean-string (replace-regexp-in-string "\n" "" string))
(length-string (length clean-string))
(number-lines (ceiling (/ (float length-string) width)))
(partition-size (ceiling (/ (float length-string) number-lines))))
(if (< length-string width)
clean-string
(string-join (seq-partition clean-string partition-size) "\n"))))
(setq personal-quote-list
(split-string (file-to-string "~/MEGA/Hobbies e Interesses/Quotes") "\n"))
(setq personal-quote-list-formatted
(mapcar
(lambda (quote)
(string-adjust-width quote (truncate (* (window-width) 0.95))))
personal-quote-list))
Setting up package and archives
(require 'package)
;; only needed for emacs versions prior to 27
(when (< emacs-major-version 27)
(package-initialize))
(add-to-list 'package-archives
'("melpa" . "http://melpa.org/packages/") t)
(add-to-list 'package-archives
'("gnu" . "https://elpa.gnu.org/packages/") t)
From the package use-package
, other packages can be installed.
Hence, ensure it is either already installed, or install it.
(unless (package-installed-p 'use-package)
(unless package-archive-contents (package-refresh-contents))
(package-install 'use-package))
Configuration for the use-package
package, which allows to manage several packages more easily by isolating the configurations in a tidy way (Github):
(eval-when-compile
;; Following line is not needed if use-package.el is in ~/.emacs.d
(require 'use-package))
;; automatically install packages not present already
(require 'use-package-ensure)
(setq use-package-always-ensure t)
;; ;; automatically update outdated packages
;; (use-package auto-package-update
;; :config
;; (setq auto-package-update-delete-old-versions t)
;; (setq auto-package-update-hide-results t)
;; (auto-package-update-maybe))
(use-package dash
:pin gnu)
(use-package seq
:pin gnu)
(use-package s)
Configurations for the dashboard
package, which replaces the splash screen with a configurable dashboard (Github):
(use-package dashboard
:init
;; set the title
(setq dashboard-banner-logo-title "Emacs Dashboard")
;; set the banner
(setq dashboard-startup-banner 'logo)
;; center content
(setq dashboard-center-content t)
;; set dashboard items
(setq dashboard-items '((bookmarks . 5)
(recents . 5)))
(use-package all-the-icons
:if (display-graphic-p))
;; use all-the-icons package
;; don't forget to M-x all-the-icons-install-fonts
(setq dashboard-icon-type 'all-the-icons)
;; add icons to the widget headings and their items
(setq dashboard-set-heading-icons t)
(setq dashboard-set-file-icons t)
;; show navigator below the banner
(setq dashboard-set-navigator t)
;; show info about the packages loaded and the init time:
(setq dashboard-set-init-info t)
;; Format: "(icon title help action face prefix suffix)"
(setq dashboard-navigator-buttons
`(;; line1
((,(all-the-icons-faicon "refresh" :height 1.1 :v-adjust 0.0)
"Reload Configs" "Reload configurations from dot files"
(lambda (&rest _) (load-file (expand-file-name "init.el" user-emacs-directory)))
nil "" ""))))
(setq dashboard-footer-messages personal-quote-list-formatted)
(setq dashboard-footer-icon (all-the-icons-faicon "quote-left"
:height 1.1
:v-adjust -0.05
:face 'font-lock-keyword-face))
:config
(dashboard-setup-startup-hook))
Configurations for the zoom
package, which automatically resizes windows according to a given ration, giving greater focus on the currently focused window (Github):
(use-package zoom
:config
(zoom-mode t)
;; resize windows according to the golden ratio
(custom-set-variables '(zoom-size '(0.618 . 0.618))))
Configurations for the solarized-theme
package, which enables automatic theme switching according to time of day (Github):
(use-package solarized-theme)
Configurations for the circadian
package, which enables automatic theme switching according to time of day (Github):
(use-package circadian
:config
(setq calendar-latitude 41.1)
(setq calendar-longitude -8.7)
(setq circadian-themes '((:sunrise . solarized-light)
("5:00" . solarized-light)
(:sunset . solarized-dark)
("18:30" . solarized-dark)))
(circadian-setup))
Configurations for the dimmer
package, which automatically dims all but the currently focused window (Github):
(use-package dimmer
:config
(dimmer-mode t)
;; set dimmer to only apply to foreground
(setq dimmer-adjustment-mode :foreground)
;; set dimmer to dim 35%
(setq dimmer-fraction 0.35))
Configurations for the helpful
package, which enhances standard help functions (Github):
(use-package helpful
:config
(global-set-key (kbd "C-h f") #'helpful-callable)
(global-set-key (kbd "C-h v") #'helpful-variable)
(global-set-key (kbd "C-h k") #'helpful-key)
(global-set-key (kbd "C-h x") #'helpful-command)
(global-set-key (kbd "C-c C-d") #'helpful-at-point))
For spell checking, I use flyspell
and aspell
.
However, flyspell
didn’t work out of the box, due to Flatpak’s containment environment.
I installed Emacs via Snap instead and it worked.
Configurations for the flyspell
package, which provides on-the-fly spell checking:
(require 'flyspell)
;; use the aspell spell checker instead of ispell
(setq ispell-program-name "aspell")
;; flyspell provides an issue message for every word it analyses
;; to avoid a slowdown, disable this feature
(setq flyspell-issue-message-flag nil)
;; set the default dictionary to British English
(setq ispell-dictionary "en_GB")
;; set the default dictionary to Portuguese
;; (setq ispell-dictionary "pt_PT")
;; enable flyspell in major modes
(add-hook 'LaTeX-mode-hook 'flyspell-mode)
(add-hook 'text-mode-hook 'flyspell-mode)
(add-hook 'prog-mode-hook 'flyspell-prog-mode)
;; run flyspell on the entire buffer after flyspell minor mode is enabled
(add-hook 'flyspell-mode-hook #'flyspell-buffer)
Now that spell checking is configured, let’s allow for spell checking of several languages at the same time.
For this, I’m using the guess-language
package.
Configuration for the guess-language
package, which automatically detects the language being typed and switches spell checker (Github):
(use-package guess-language
:ensure t
:init
;; guess language in major modes
(add-hook 'LaTeX-mode-hook #'guess-language-mode)
(add-hook 'text-mode-hook #'guess-language-mode)
(add-hook 'prog-mode-hook #'guess-language-mode)
:config
;; guess languages between portuguese and british
(setq guess-language-languages '(en pt))
(setq guess-language-langcodes
'((en . ("en_GB" "british" "🇬🇧" "English"))
(pt . ("pt_PT" "portuguese" "🇵🇹" "Português"))))
;; set the minimal length a paragraph needs to have before guess-language-mode changes to its language
(setq guess-language-min-paragraph-length 35)
;; automatically run flyspell-buffer when languages change
;; (add-hook 'guess-language-after-detection-functions
;; (lambda (lang beg end)
;; (flyspell-region beg end)))
)
Note that guess-language
inspects a paragraph in order to detect language.
Hence, the texts in different languages must have at least a empty line between them.
Configurations for the rainbow-mode
package, which sets the background colour of strings to match the string’s colour name (Github):
(use-package rainbow-mode
:hook (emacs-lisp-mode text-mode lisp-mode))
Configurations for the aggressive-indent
package, which automatically inserts indentation according to the language and scope (Github):
(use-package aggressive-indent
:config
;; (add-to-list 'aggressive-indent-excluded-modes 'emacs-lisp-mode)
(global-aggressive-indent-mode 1))
Configurations for the highlight-indent-guides
package, which shown indentation guides (Github):
(use-package highlight-indent-guides
:config
(add-hook 'prog-mode-hook 'highlight-indent-guides-mode)
(setq highlight-indent-guides-method 'character))
Configurations for the company
package, which provides autocompletion tooltips (Github webpage):
(use-package company
:pin gnu
:config
;; (setq company-idle-delay nil)
(setq company-dabbrev-downcase nil)
(add-hook 'after-init-hook 'global-company-mode))
Configurations for the origami
package, which allows code blocks to be folded (Github):
(use-package origami
:requires (dash s)
:config
(global-origami-mode))
Configurations for the haskell-mode
package, which provides Haskell keyword highlighting (Github):
(use-package haskell-mode)
Configurations for the markdown-mode
package, which provides markdown keyword highlighting (Github):
(use-package markdown-mode
:mode (("README\\.md\\'" . gfm-mode)
("TODO\\.md\\'" . gfm-mode)
("\\.md\\'" . markdown-mode)
("\\.markdown\\'" . markdown-mode))
:init (setq markdown-command "pandoc"))
;; show whitespaces as dots
;; (add-hook 'latex-mode-hook 'whitespace-mode)
;; prevent truncating lines in org mode; similar to word-wrap
(setq org-startup-truncated nil)
;; open files with unfolded headings
(setq org-startup-folded nil)
Configurations for the whisper speech-to-text engine:
(add-to-list 'load-path (expand-file-name "packages/whisper" user-emacs-directory))
(use-package whisper
:load-path "~/.emacs.d/packages/whisper"
:bind ("C-H-r" . whisper-run)
:config
(setq whisper-install-directory "~/.local/lib"
whisper-language "en"
whisper-model "base" ;; model options: tiny, base, small, medium, large
whisper-translate nil
whisper-enable-speed-up nil))