There’s happiness :), and there’s gleemacs =D.
Last revised and exported on 2025-12-05 10:32:16 +1100}.
This is my personal configuration for the awesome GNU Emacs! I built it from the
ground up with two blank files in a directory named ~/.gleemacs, a metric shit
tonne of reading through C-h v/f/P/m/k, and the equivalent volumetric weight in
coffee. Eventually it grew into this living document. I created it because I
want to know my setup inside and out. After 5 weeks with DOOM Emacs, I had a
clear idea of both what was possible with Emacs and what I needed in my
workspace. So I set out to write my own and started with research and review of
configs in the wild.
For me, those configurations were instrumental in learning vanilla Emacs. As I
read through them, I would C-h each variable or function with my init files in
a split window. If it met my needs, improved performance, or made me smile, it
went in. This proved extremely useful for two reasons: I gained an understanding
of what settings I really needed and what they did, while learning to use Emacs
in it’s vanilla state.
This experience was a positive Emacs-loop. The most useful of all being my finagling of the built-in help functionality. Once you learn it, all of a sudden the thing you want to change can be found on your own. In my opinion this is more valuable than learning lisp itself when you’re new to Emacs (or maybe this is just an excuse for getting lost in parentheses…).
gleemacs is thus built on the shoulders of giants. It’s the result of research, curation, and personalisation. It’s performant, persistent, and hygienic. It’s easy to maintain and traverse, and is fuelled by practicality, essentialism, and good old fashioned fun.
Please enjoy :)
Table of Contents
- Overview
- Preface
- Tangle me
- Prepare for lisp off! (
early-init.el) - Let’s get you ready for work (
init.el) - Welcome to the house of glee (gleemacs modules)
gleemacs-authgleemacs-completiongleemacs-denotegleemacs-diredgleemacs-eglotgleemacs-emailgleemacs-essentialsgleemacs-flymakegleemacs-gitgleemacs-helpersgleemacs-ircgleemacs-keybindingsgleemacs-markdowngleemacs-languagesgleemacs-orggleemacs-pythongleemacs-rssgleemacs-searchgleemacs-spellcheckgleemacs-terminalgleemacs-window
- Cheers and Inspiration
Overview
gleemacs has been built for:
-
Programming: IDE-like functionality for Python with
basedpyright. Setup for handling Docker, bash,PKGBUILDfiles, and various data serialisation languages. Efficient version control withmagit. -
Writing and note taking:
organddenotefor efficient note taking and organised writing. Personal and project specific capture templates. -
Communication and information: Emails using
notmuch,smtpmailand anmbsyncbackend. RSS reading withelfeed. IRC witherc. -
Daily useage: It’s fast! File editing persistence and safety without pollution. Legible in all lighting conditions with
modus-themes. Simplified, informative modeline.
Preface
Why
In essence, gleemacs exists because I want to know how my setup works. The
goal is to remove complexity while remaining easy to configure, maintain, and
traverse. When things go wrong, I can fix them without relying on someone else
(thanks, C-h!). After reviewing a lot of configs in the wild from Protesilaos,
Purcell, and Cherti, it didn’t feel like a mission to build what I had in mind.
Context
- Experience with GNU Emacs at start of writing: ~6 weeks
Philosophy
Ice Ice Baby!
gleemacs is focused on vanilla GNU Emacs in terms of bindings and packages.
Mainly because I want to be able to use Emacs (generally) across systems without
too much friction, and it’s my favourite flavour of ice cream. The fine print
being: ’leader key’ functionality is setup with the prefix C-c using
general.el (see the gleemacs-keybindings module), but the goal is to be as
close to vanilla or default configurations as possible. That means you’ll still
need to manually M-x hanoi, sorry!
Another reason for this is performance. For example, using the built in modeline
compared to using the doom-modeline package came with significant startup
improvements.
Of course, I will utilise third party packages where their use far outweighs my efforts in reproducing the functionality. In some cases, like the mode line for example, investing effort in learning in-built functionality was worth the performance gains.
I’m happy to rely on the community to provide functionality that the reproduction of which is well beyond my pay grade. At the end of the day I don’t want to engage in busy work either. I actually want to use Emacs, not infinitely configure it.
Performance
Inspiration is taken from existing configurations to build a solid and performant setup. Typical startup improvements such as garbage collection, native compilation of packages, and other refinements are found in early-init.el. Dashboard functionality has also been implemented using the dashboard package located in the gleemacs-essentials module. I rarely use the *scratch* buffer and like the utility that a dashboard brings with it.
Other notable features include:
- the use of
gcmhto manage garbage collection post start-up, - complete disabling of the vanilla startup screen (in addition to inhibiting it), and
- run of the mill removal of GUI features, including GUI/GTK-based prompts.
Experimentation with different settings by addition, subtraction, or modification, did not yield a performance improvement that warranted inclusion. In my useage startup times vary from 0.3-0.4s and I’m happy with that!
Persistence
My mind works better when I’m able to get right back into something in the exact
context and configuration that I left it in. To achieve this, a combination of
recentf, savehist, save-place, undo-fu (see gleemacs-essentials), and
persp-mode are used (see gleemacs-window). The loading of perspectives on
startup are up to the user via C-c p l, as I prefer to have the option to
resume the perspective I was in or to start with a blank canvas.
- 2025-11-17:
persp-modeis currently disabled as I didn’t end up using it to it’s full potential. I’m now trying outtab-barto visually separate workspaces which in practice suits my workflow better.
Hygiene
Persistence requires writing data to disk, and by default Emacs thinks any
working directory is it’s oyster, leading to a lot of stray #files# and a
cluttered .emacs.d folder. To keep our files tidy and organised, we disable
lockfiles in init.el, and utilise the no-littering package from the
gleemacs-essentials module to manage persistent data.
Privacy
To preserve the privacy of data like email addresses, IRC logins, and other
private information, gleemacs makes us of the auth-sources (see
gleemacs-auth). The idea is to be able to share or publicly store
configurations without leaking sensitive or otherwise private data. Additional
security enhancements are found in security settings which mainly apply to IRC
and self-hosted web service setups (e.g., email with self-signed certificates).
Implementing out of the box reliance on auth-sources and the .authinfo.gpg
file encourages best practices when it comes to distributing sensitive
information. I also considered integrating this functionality using pass, but
it’s overkill for my requirements at this stage.
Tangle me
- To evaluate, simply run
C-c C-v C-t
Prepare for lisp off! (early-init.el)
The early initialisation phase contains performance optimisations, disables the
inbuilt GUI, sets up package archives and use-package, and improves some basic
security settings. Prot graciously explained to me that this phase initialises
prior to Emacs drawing it’s frame, and I’ve therefore tried to arrange the
settings with this in mind.
These settings have been curated from other configs that offer the greatest
performance benefits. As a result, our early-init.el is rather short, but it
provides excellent performance!
Header
;;; early-init.el --- Early Init -*- no-byte-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: "0.1.0"
;;; Commentary:
;;
;; The `early-init' is will run prior to Emacs rendering it's initial frame.
;; Therefore, strictly legal, performance enhancing substances should go here.
;;
;; Slow start up times make Sam go something something..
;;; Code:
Performance Handling
In my tinkering, the following settings were the most important when trying to improve startup times. As I briefly mentioned earlier, experimentation with a range of settings seemingly provided little to no benefit. Additionally, a lot of settings in use in other configs seem to be the default for Emacs when reading about them in C-h v, so I decided to leave them out.
We do not reset our garbage collection settings because that’s taken care of by gcmh after early initialisation. Read more about this in the gcmh section.
;; ; Performance Handling ---------------
;; Temporarily increase the garbage collection threshold. These
;; changes help shave off about half a second of startup time. The
;; `most-positive-fixnum' is DANGEROUS AS A PERMANENT VALUE. See the
;; `emacs-startup-hook' a few lines below for what I actually use.
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.5)
;; Same idea as above for the `file-name-handler-alist' and the
;; `vc-handled-backends' with regard to startup speed optimisation.
;; Here I am storing the default value with the intent of restoring it
;; via the `emacs-startup-hook'.
(defvar gleemacs--file-name-handler-alist file-name-handler-alist)
(defvar gleemacs--vc-handled-backends vc-handled-backends)
(setq file-name-handler-alist nil
vc-handled-backends nil)
;; Process performance tuning
(setq read-process-output-max (* 4 1024 1024))
;; ; Restore defaults after init
(add-hook 'emacs-startup-hook
(lambda ()
(setq file-name-handler-alist gleemacs--file-name-handler-alist
vc-handled-backends gleemacs--vc-handled-backends)))
Frame settings
Mostly standard Emacsen settings here, such as disabling GUI and GTK related prompts, and preventing (not just inhibiting) the vanilla Emacs startup screen. There’s not much to see here. ;)
;; ; UI Config ---------------
;; Don't show the splash screen
(setq inhibit-startup-message t)
;; Suppress the vanilla startup screen completely. We've disabled it with
;; `inhibit-startup-screen', but it would still initialize anyway.
(advice-add 'display-startup-screen :override #'ignore)
;; Remove "For information about GNU Emacs..." message at startup
;; Courtesy of James Cherti
(advice-add 'display-startup-echo-area-message :override #'ignore)
(tool-bar-mode -1) ; Disable toolbar
(scroll-bar-mode -1) ; Disable scrollbar
(menu-bar-mode -1) ; Disable menu bar
(set-fringe-mode 10) ; Give fringe some space
(column-number-mode)
(global-display-line-numbers-mode 1) ; Show line numbers
;; Disable GTK/GUI prompts and limit to minibuffer y/n
(setq use-dialog-box nil)
(setq use-file-dialog nil)
(setq use-short-answers t)
;; Frame settings
;; Courtesy of Protesilaos Stavrou
(setq frame-resize-pixelwise t
frame-inhibit-implied-resize 'force
frame-title-format '("%b")
ring-bell-function 'ignore
inhibit-splash-screen t
inhibit-startup-screen t
inhibit-x-resources t
inhibit-startup-echo-area-message user-login-name ; read the docstring
inhibit-startup-buffer-menu t)
Packages
;; Prefer newest compiled files
(setq load-prefer-newer t)
;; Initialise installed packages
(setq package-enable-at-startup nil)
Footer
;; End:
;;; early-init.el ends here
Let’s get you ready for work (init.el)
The initialisation phase (also see early-init) is responsible for ensuring that
Emacs is performant and establishing a sound vanilla experience. init.el
notably contains packaging setup, garbage collection, and our aesthetic settings
like selecting a theme and our preferred font.
The focus during design was to minimise as much as possible any opinionated influence outside of aesthetic choice. I learnt so much about Emacs reading it’s own documentation when researching other configurations. So gleemacs without modules is a great environment for learning GNU Emacs without it being too opinionated.
Additional functionality is added via gleemacs modules rather than managing one
large init.el file. This centralises configuration to the respective modules
and allows for easy maintenance and efficient customisation.
Header
;;; init.el --- Initialise Gleemacs -*- no-byte-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;; Now that the frame is rendered, we can start resetting some dangerous
;; performance settings from the `early-init' stage, and begin adding some
;; basic functionality. In the case of `gleemacs', this means establish a
;; sound GNU Emacs environment that is perfect place to learn vanilla Emacs.
;;
;; If you were expecting chocolate Emacs, you will sadly be disappointed :(
;; Tea and biscuits are at the back.
;;; Code:
Package management
Enable native package compilation
Here we enable native package compilation just in time compiling for significant performance improvements. Additionally, we silence any compiler errors on startup that, as I understand, are only useful to Emacs developers and not a real concern for users.
;; ; Package Management ---------------
;; Enable async native compilation
(setq package-native-compile t
;; native-comp-deferred-compilation t ; obsolete
native-comp-jit-compilation t ; supersedes above
native-comp-async-report-warnings-errors nil)
Setup package
(require 'package)
;; Initialise installed packages
(setq package-enable-at-startup t)
… And prioritise our preferred archives.
;; Don't register packages as projects
(setq package-vc-register-as-project nil) ; Emacs 30
;; Also read: <https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/>
(setq package-archives
'(("gnu-elpa" . "https://elpa.gnu.org/packages/")
("gnu-elpa-devel" . "https://elpa.gnu.org/devel/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
("melpa" . "https://melpa.org/packages/")))
;; Highest number gets priority (what is not mentioned has priority 0)
(setq package-archive-priorities
'(("gnu-elpa" . 3)
("melpa" . 2)
("nongnu" . 1)))
(package-initialize)
(unless package-archive-contents
(package-refresh-contents))
Setup use-package
We then setup use-package which is used exclusively throughout gleemacs from this point forwards.
;; Initialize use-package on non-Linux platforms
(unless (package-installed-p 'use-package)
(package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure t)
(setq use-package-enable-imenu-support t)
Security settings
It’s notable that these settings may require additional configuration for self-signed certificates. In practical terms, this has only impacted localhost addressed web services in my experience, such as SMTP.
;; ; Security ---------------
;; Courtesy of James Cherti
(setq gnutls-verify-error t) ; Prompts user if there are certificate issues
(setq tls-checktrust t) ; Ensure SSL/TLS connections undergo trust verification
(setq gnutls-min-prime-bits 3072) ; Stronger GnuTLS encryption
Setup *scratch* buffer mode
Setting the *scratch* buffer’s major mode to fundamental-mode improves startup performance effectively by limiting the dependencies (e.g., flymake) for a basic startup. gleemacs is also configured to display a simple dashboard on startup.
;;; Set `*scratch*' buffer to `fundamental-mode'
(setq initial-major-mode 'fundamental-mode)
Dynamic garbage collection with gcmh
gmch dynamically adjusts garbage collection settings based on an idle delay.
In my experience the use of this package resulted in faster startup times than
restoring the garbage collection defaults in init.el. It also allows you to
adjust the minimum and maximum gc-cons-threshold in a similar fashion to
Emacs, so it felt reasonable to include this as a matter of performance.
;; ; Performance Tuning ---------------
;; This handles our garbage collection automatically when we're not using
;; Emacs. While I know that's insane to not use Emacs, but just know our trash
;; will be collected promptly, and a stated delay (inspiration: Local Council).
(use-package gcmh
:hook (after-init . gcmh-mode)
:diminish gcmh-mode
:custom
(gcmh-high-cons-threshold (* 128 1024 1024))
(gcmh-idle-delay 5))
Setup modus-themes
Modus strikes the perfect balance between no syntax and looking like the results of Homer’s make-up gun experiment.
Prot provides functionality that allows for suitable themes in any environment
with the mode-themes-toggle and mode-themes-rotate functions. In bright
daylight conditions I’ve found modus-operandi to be the most legible. As the
golden hour approaches or when it’s overcast, I’ll move to the tinted variant.
Finally, for low light I prefer modus-vivendi-tinted because my eyes seem to
suffer less stress than the non-tinted variant.
The default mapping for F5 has been swapped to modus-themes-rotate so we can
rotate between the three themes as needed throughout the day. This is done
because -toggle (for now!) only supports two variants [which to clarify seems
reasonable given ’toggle’ implies binary functionality, whereas ’rotate’ implies
multiple options].
;; ; UI Config ---------------
;; Theme Setup
;; Modus Themes /co Protesilaos Stavrou
(use-package modus-themes
:ensure t
:demand t
:init
;; Starting with version 5.0.0 of the `modus-themes', other packages
;; can be built on top to provide their own "Modus" derivatives.
;; For example, this is what I do with my `ef-themes' and
;; `standard-themes' (starting with versions 2.0.0 and 3.0.0,
;; respectively).
;;
;; The `modus-themes-include-derivatives-mode' makes all Modus
;; commands that act on a theme consider all such derivatives, if
;; their respective packages are available and have been loaded.
;;
;; Note that those packages can even completely take over from the
;; Modus themes such that, for example, `modus-themes-rotate' only
;; goes through the Ef themes (to this end, the Ef themes provide
;; the `ef-themes-take-over-modus-themes-mode' and the Standard
;; themes have the `standard-themes-take-over-modus-themes-mode'
;; equivalent).
;;
;; If you only care about the Modus themes, then (i) you do not need
;; to enable the `modus-themes-include-derivatives-mode' and (ii) do
;; not install and activate those other theme packages.
;; (modus-themes-include-derivatives-mode 1)
:bind
(("<f5>" . modus-themes-rotate)
("C-<f5>" . modus-themes-select)
("M-<f5>" . modus-themes-load-random))
:config
;; Your customizations here:
(setq modus-themes-custom-auto-reload nil
modus-themes-to-toggle '(modus-operandi-tinted modus-vivendi-tinted)
modus-themes-to-rotate '(modus-operandi
modus-operandi-tinted
modus-vivendi-tinted)
modus-themes-mixed-fonts t
modus-themes-variable-pitch-ui t
modus-themes-italic-constructs t
modus-themes-bold-constructs t
modus-themes-completions '((t . (bold)))
modus-themes-prompts '(bold))
;; modus-themes-headings
;; '((agenda-structure . (variable-pitch light 2.2))
;; (agenda-date . (variable-pitch regular 1.3))
;; (t . (regular 1.15))))
(setq modus-themes-common-palette-overrides nil)
;; Finally, load your theme of choice (or a random one with
;; `modus-themes-load-random', `modus-themes-load-random-dark',
;; `modus-themes-load-random-light').
(modus-themes-load-theme 'modus-operandi))
Configure custom fonts
I know exactly what you’re thinking right now: “this guy is cosplaying a Greek philosopher!” Not quite, but his influence is clear, and I cannot fault Prot’s aesthetic taste and focus on legibility. Aporetic is perfect for my needs because of limited screen space and utilise window splits heavily in my workflow. Years ago I tried Iosevka, but I could never get used to it. I would revert to Hack or JetBrains Mono for their legibility, until I found Aporetic: a compact Hack!
;; Setup fonts
(set-face-attribute 'default nil
:height 130 :weight 'normal :family "Aporetic Sans Mono")
(set-face-attribute 'fixed-pitch nil
:family "Aporetic Serif Mono")
(set-face-attribute 'variable-pitch nil
:family "Aporetic Serif")
Disable line numbers (mode specific)
Don’t like line numbers in some mode? No worries, here we define a list of -mode-hook to disable line numbers. To find a mode, C-h m in the buffer you want to check; once you know the mode, you can search using C-h f for the relevant hook function to append below. And just like that… They were gone!
;; Disable line numbers for some modes
(dolist (mode '(org-mode-hook
term-mode-hook
shell-mode-hook
vterm-mode-hook
notmuch-hello-mode-hook
notmuch-show-mode-hook
notmuch-tree-mode-hook
elfeed-show-mode-hook
elfeed-search-mode-hook
treemacs-mode-hook
eshell-mode-hook))
(add-hook mode (lambda () (display-line-numbers-mode 0))))
Set default fill-column width
Set the default line width to 80 characters. If you write a particularly long
line in org-mode, you can M-q which automatically breaks up the line at
column number 80. See C-h k M-q for mode-specific information about this
feature.
2025-11-17: I now use the hook (text-mode . turn-on-auto-fill) which handles
this automatically in text-mode buffers. See gleemacs-languages/text-mode for
more information and settings.
(setq-default fill-column 80)
which-key was it again?
This is an extremely useful package that displays the commands available to you based on which key you press next. It’s particularly helpful as you get accustomed to Emacs, and more generally when you forget the keymap you need once in a blue moon. I find this package most practical when mappings are deeply nested, such as Org’s C-c.
Eventually these things become second nature and you hardly ever see the display initiate, but like a good friend, it’s always there when you need it!
;; ; Which Key -------------------
(use-package which-key
:ensure nil ; built in!
:diminish which-key-mode
:hook (after-init . which-key-mode)
:config
(setq which-key-prefix-prefix "+")
(setq which-key-idle-delay 1)
(setq which-key-add-column-padding 2)
(setq which-key-max-display-columns 4)
(setq which-key-idle-secondary-delay 0.25))
Emacs tweaks
Disable lockfiles
For my work flow lock files aren’t necessary, so that functionality is disabled here.
;; ; Emacs Tweaks -------------------
;; disable backup and lock files
;; (setq make-backup-files nil)
;; (setq backup-inhibited nil) ; Not sure if needed, given `make-backup-files'
(setq create-lockfiles nil)
custom.el be gone!
By default Emacs writes to init.el even if you haven’t made any changes using M-x customize and dirties our configuration files unnecessarily. Therefore we disable the functionality by piping the custom.el file to a temporary file.
For users (heathens!!!) who want to use this functionality the relevant settings are provided in comments below.
;; Completely disable `custom.el', thanks Prot!
;; If you use `custom.el', you should comment this out:
(setq custom-file (make-temp-file "emacs-custom-"))
;; Send custom to `<USER-EMACS-DIR>/custom.el':
;; (setq custom-file (expand-file-name "custom.el" user-emacs-directory))
;; This just sets up the custom file location and stops emacs from writing
;; the crap in `init.el'. To actually utilise the `custom.el', uncomment:
;; (when (file-exists-p custom-file)
;; (load custom-file))
Require gleemacs modules
;; ; Add gleemacs module support
(mapc
(lambda (string)
(add-to-list 'load-path (locate-user-emacs-file string)))
'("gleemacs-modules"))
;; Now we require more glee...
(require 'gleemacs-essentials)
(require 'gleemacs-completion)
(require 'gleemacs-search)
(require 'gleemacs-dired)
(require 'gleemacs-window)
(require 'gleemacs-git)
(require 'gleemacs-org)
(require 'gleemacs-denote)
(require 'gleemacs-markdown)
(require 'gleemacs-rss)
(require 'gleemacs-terminal)
(require 'gleemacs-languages)
(require 'gleemacs-eglot)
(require 'gleemacs-flymake)
(require 'gleemacs-python)
(require 'gleemacs-auth)
(require 'gleemacs-email)
(require 'gleemacs-irc)
(require 'gleemacs-spellcheck)
(require 'gleemacs-keybindings)
;;; init.el ends here
Welcome to the house of glee (gleemacs modules)
gleemacs-auth
To preserve the privacy of data like email addresses, IRC logins, and other
private information, gleemacs makes use of the auth-source package. The idea
is to be able to share or publicly store configurations without leaking
sensitive or otherwise private data.
Use the helper function gleemacs-helper-auth-get-field to easily make use of
your .authinfo.gpg file as needed in this config.
[ This function and idea is courtesy of Protesilaos Stavrou. See https://protesilaos.com/emacs/dotemacs#h:2fca749c-a84e-415c-9ac0-b1518d06fa51 ]
Header
;;; gleemacs-auth.el --- gpg more like gp-Glee! -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;; This module expects that you have setup an `.authinfo.gpg' file
;; in the correct format. It's main purpose is to provide a safe
;; way for users to store their private emails in public git repos
;; without leaking them.
;;; Code:
auth-source
(use-package auth-source
:ensure nil
:defer t
:config
(setq auth-sources '("~/.authinfo.gpg")))
Set user-full-name
(setq user-full-name "Sam Sinclair")
Set user-email-address
(require 'gleemacs-helpers)
;; ;; Set mail address on notmuch startup
(add-hook 'notmuch-hello-mode-hook
(lambda ()
(let ((email (gleemacs-helper-auth-get-field "public" :user)))
(setq user-mail-address email))))
;; 100) ; High priority
Footer
;; ; Now providing gpglee...
(provide 'gleemacs-auth)
;;; gleemacs-auth.el ends here
gleemacs-completion
Header
;;; gleemacs-completion.el --- Completion toolings for gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
vertico
;; ; Vertico!
(use-package vertico
:ensure t
:hook (after-init . vertico-mode)
:config
(setq vertico-scroll-margin 0)
(setq vertico-resize t)
(setq vertico-cycle t))
orderless
orderless is a package that provides fuzzy search and is extremely useful,
especially when looking for a particular command, function, or file that you
can’t quite remember the name of. For how this is used, see the minibuffer section.
;; ; Where we're going, we don't need no orders
(use-package orderless
:ensure t
:config
;; Remember to check my `completion-styles' and the
;; `completion-category-overrides'.
(setq orderless-matching-styles '(orderless-prefixes
orderless-regexp))
(setq orderless-smart-case 1)
;; SPC should never complete: use it for `orderless' groups.
;; The `?' is a regexp construct.
:bind ( :map minibuffer-local-completion-map
("SPC" . nil)
("?" . nil)))
minibuffer
Here we configure our completion styles for the minibuffer. Completion styles
are prioritised in the order they are written, so if a match isn’t found by the
first style, the next style is used, until no matches are returned. Where a
completion-category-overrides is not set for a category, the default
completion-styles will be used.
This took effort to get to the way I like, but it was worth it.
;;; Minibuffer completion setup
;; Courtesy of Protesilaos Stavrou
(use-package minibuffer
:ensure nil
:config
(setq completion-styles '(basic orderless))
;; (setq completion-category-defaults nil)
(setq completion-category-overrides
;; NOTE 2021-10-25: I am adding basic' because it works better as a
;; default for some contexts. Read:
;; <https://debbugs.gnu.org/cgi/bugreport.cgi?bug=50387>.
;;
;; partial-completion' is a killer app for files, because it
;; can expand ~/.l/s/fo to ~/.local/share/fonts.
;;
;; If basic' cannot match my current input, Emacs tries the
;; next completion style in the given order. In other words,
;; orderless' kicks in as soon as I input a space or one of its
;; style dispatcher characters.
'((file (styles . (basic partial-completion orderless)))
(bookmark (styles . (basic substring)))
(library (styles . (basic substring)))
(embark-keybinding (styles . (basic substring)))
(imenu (styles . (basic substring orderless)))
(consult-location (styles . (basic substring orderless)))
(kill-ring (styles . (emacs22 orderless)))
(eglot (styles . (emacs22 substring orderless))))))
;; ; Ignore letting casing in minbuffer
(setq read-buffer-completion-ignore-case t)
(setq-default case-fold-search t) ; For general regexp
(setq read-file-name-completion-ignore-case t)
dabbrev
To quote C-h P dabbrev:
The purpose with this package is to let you write just a few characters of words you’ve written earlier to be able to expand them.
To expand a word, just put the point right after the word and press M-/ (dabbrev-expand) or M-C-/ (dabbrev-completion).
Couldn’t have put it better myself! Unlike other configs dabbrev-completion is
not automatic, as in, you must activate it yourself with M-C-/. I prefer this
as it can be distracting and annoying to be constantly suggested abbreviations
that are useless or unwanted.
;; ; Dynamic Abbreviation aka dabbrev
;;;; `dabbrev' (dynamic word completion (dynamic abbreviations))
(use-package dabbrev
:ensure nil
:commands (dabbrev-expand dabbrev-completion)
:config
(setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_")
(setq dabbrev-abbrev-skip-leading-regexp "[$*/=~']")
(setq dabbrev-backward-only nil)
(setq dabbrev-case-distinction 'case-replace)
(setq dabbrev-case-fold-search nil)
(setq dabbrev-case-replace 'case-replace)
(setq dabbrev-check-other-buffers t)
(setq dabbrev-eliminate-newlines t)
(setq dabbrev-upcase-means-case-search t)
(setq dabbrev-ignored-buffer-modes
'(archive-mode image-mode docview-mode pdf-view-mode)))
corfu
(use-package corfu
:ensure t
:hook (after-init . global-corfu-mode)
;; I also have (setq tab-always-indent 'complete) for TAB to complete
;; when it does not need to perform an indentation change.
:bind (:map corfu-map ("<tab>" . corfu-complete))
:config
(setq corfu-preview-current nil)
(setq corfu-min-width 20)
(setq corfu-popupinfo-delay '(1.25 . 0.5))
(corfu-popupinfo-mode 1) ; shows documentation after `corfu-popupinfo-delay'
;; Sort by input history (no need to modify `corfu-sort-function').
(with-eval-after-load 'savehist
(corfu-history-mode 1)
(add-to-list 'savehist-additional-variables 'corfu-history)))
consult
(use-package consult
:ensure t
:hook (completion-list-mode . consult-preview-at-point-mode)
:bind
( :map global-map
("M-g M-g" . consult-goto-line)
("M-s M-b" . consult-buffer)
("M-s M-f" . consult-find)
("M-s M-g" . consult-grep)
("M-s M-h" . consult-history)
("M-s M-i" . consult-imenu)
("M-s M-l" . consult-line)
("M-s M-m" . consult-mark)
("M-s M-y" . consult-yank-pop)
("M-s M-s" . consult-outline)
:map consult-narrow-map
("?" . consult-narrow-help))
:init
;; Optionally configure the register formatting. This improves the register
(setq register-preview-delay 0.5
register-preview-function #'consult-register-format)
;; Optionally tweak the register preview window.
(advice-add #'register-preview :override #'consult-register-window)
;; Use Consult to select xref locations with preview
(setq xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref)
:config
(setq consult-line-numbers-widen t)
;; (setq completion-in-region-function #'consult-completion-in-region)
(setq consult-async-min-input 3)
(setq consult-async-input-debounce 0.5)
(setq consult-async-input-throttle 0.8)
(setq consult-narrow-key nil)
(setq consult-find-args
(concat "find . -not ( "
"-path */.git* -prune "
"-or -path */.cache* -prune )"))
(setq consult-preview-key 'any)
(setq consult-project-function nil) ; always work from the current directory (use `cd' to switch directory)
(add-to-list 'consult-mode-histories '(vc-git-log-edit-mode . log-edit-comment-ring))
(require 'consult-imenu)) ; the `imenu' extension is in its own file
embark
;; Extended minibuffer actions and more (embark.el)
(use-package embark
:ensure t
:bind
( :map minibuffer-local-map
("C-c C-c" . embark-collect)
("C-c C-e" . embark-export)))
embark-consult
;; Needed for correct exporting while using Embark with Consult
;; commands.
(use-package embark-consult
:ensure t
:after (embark consult))
marginalia
;;; Detailed completion annotations (marginalia.el)
(use-package marginalia
:ensure t
:commands (marginalia-mode marginalia-cycle)
:init (marginalia-mode)
:config
(setq marginalia-max-relative-age 0)) ; absolute time
Nerd icons for marginalia
;; Nerd icons for marginalia
(use-package nerd-icons-completion
:after marginalia
:init (nerd-icons-completion-mode))
yasnippet
;; YASnippet is a template system designed that enhances text editing by
;; enabling users to define and use snippets. When a user types a short
;; abbreviation, YASnippet automatically expands it into a full template, which
;; can include placeholders, fields, and dynamic content.
;;
;; Courtesy of James Cherti
(use-package yasnippet
:ensure t
:commands (yas-minor-mode
yas-global-mode)
:diminish (yas-minor-mode
yas-global-mode)
:hook
(after-init . yas-global-mode)
:custom
(yas-also-auto-indent-first-line t) ; Indent first line of snippet
(yas-also-indent-empty-lines t)
(yas-snippet-revival nil) ; Setting this to t causes issues with undo
(yas-wrap-around-region nil) ; Do not wrap region when expanding snippets
;; (yas-triggers-in-field nil) ; Disable nested snippet expansion
;; (yas-indent-line 'fixed) ; Do not auto-indent snippet content
;; (yas-prompt-functions '(yas-no-prompt)) ; No prompt for snippet choices
:init
;; Suppress verbose messages
(setq yas-verbosity 0))
yasnippet-snippets
;; The official collection of snippets for yasnippet.
(use-package yasnippet-snippets
:ensure t
:after yasnippet)
;; YASnippet is a template system designed that enhances text editing by
;; enabling users to define and use snippets. When a user types a short
;; abbreviation, YASnippet automatically expands it into a full template, which
;; can include placeholders, fields, and dynamic content.
;;
;; Courtesy of James Cherti
(use-package yasnippet
:ensure t
:commands (yas-minor-mode
yas-global-mode)
:diminish (yas-minor-mode
yas-global-mode)
:hook
(after-init . yas-global-mode)
:custom
(yas-also-auto-indent-first-line t) ; Indent first line of snippet
(yas-also-indent-empty-lines t)
(yas-snippet-revival nil) ; Setting this to t causes issues with undo
(yas-wrap-around-region nil) ; Do not wrap region when expanding snippets
;; (yas-triggers-in-field nil) ; Disable nested snippet expansion
;; (yas-indent-line 'fixed) ; Do not auto-indent snippet content
;; (yas-prompt-functions '(yas-no-prompt)) ; No prompt for snippet choices
:init
;; Suppress verbose messages
(setq yas-verbosity 0))
Footer
;; ; Now providing glee...
(provide 'gleemacs-completion)
;;; gleemacs-completion.el ends here
gleemacs-denote
Header
;;; gleemacs-denote.el --- Denote setup for gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;; Simply the best implementation of plain text note taking.
;;; Code:
;; Remember that the website version of this manual shows the latest
;; developments, which may not be available in the package you are
;; using. Instead of copying from the web site, refer to the version
;; of the documentation that comes with your package. Evaluate:
;;
;; (info "(denote) Sample configuration")
denote
;; ; Denote!
(use-package denote
:ensure t
:hook
( ;; If you use Markdown or plain text files, then you want to make
;; the Denote links clickable (Org renders links as buttons right
;; away)
(text-mode . denote-fontify-links-mode-maybe)
;; Apply colours to Denote names in Dired. This applies to all
;; directories. Check `denote-dired-directories' for the specific
;; directories you may prefer instead. Then, instead of
;; `denote-dired-mode', use `denote-dired-mode-in-directories'.
(dired-mode . denote-dired-mode))
:bind
;; Denote DOES NOT define any key bindings. This is for the user to
;; decide. For example:
;; ( :map global-map
;; ("C-c n n" . denote)
;; ("C-c n d" . denote-dired)
;; ("C-c n g" . denote-grep)
;; ;; If you intend to use Denote with a variety of file types, it is
;; ;; easier to bind the link-related commands to the `global-map', as
;; ;; shown here. Otherwise follow the same pattern for `org-mode-map',
;; ;; `markdown-mode-map', and/or `text-mode-map'.
;; ("C-c n l" . denote-link)
;; ("C-c n L" . denote-add-links)
;; ("C-c n b" . denote-backlinks)
;; ("C-c n q c" . denote-query-contents-link) ; create link that triggers a grep
;; ("C-c n q f" . denote-query-filenames-link) ; create link that triggers a dired
;; ;; Note that `denote-rename-file' can work from any context, not just
;; ;; Dired bufffers. That is why we bind it here to the `global-map'.
;; ("C-c n r" . denote-rename-file)
;; ("C-c n R" . denote-rename-file-using-front-matter)
;; Key bindings specifically for Dired.
( :map dired-mode-map
("C-c C-d C-i" . denote-dired-link-marked-notes)
("C-c C-d C-r" . denote-dired-rename-files)
("C-c C-d C-k" . denote-dired-rename-marked-files-with-keywords)
("C-c C-d C-R" . denote-dired-rename-marked-files-using-front-matter))
:config
;; Remember to check the doc string of each of those variables.
(setq denote-directory (expand-file-name "~/org/"))
(setq denote-save-buffers nil)
(setq denote-known-keywords '("emacs"))
(setq denote-infer-keywords t)
(setq denote-sort-keywords t)
(setq denote-prompts '(title keywords))
(setq denote-excluded-directories-regexp nil)
(setq denote-keywords-to-not-infer-regexp nil)
(setq denote-rename-confirmations '(rewrite-front-matter modify-file-name))
;; Pick dates, where relevant, with Org's advanced interface:
(setq denote-date-prompt-use-org-read-date t)
;; Automatically rename Denote buffers using the `denote-rename-buffer-format'.
(denote-rename-buffer-mode 1))
denote-journal
;; ; A journal a day keeps the doctor away
(use-package denote-journal
:ensure t
:after denote
;; Bind those to some key for your convenience.
:commands ( denote-journal-new-entry
denote-journal-new-or-existing-entry
denote-journal-link-or-create-entry )
;; :bind
:hook (calendar-mode . denote-journal-calendar-mode)
:config
;; Use the "journal" subdirectory of the `denote-directory'. Set this
;; to nil to use the `denote-directory' instead.
(setq denote-journal-directory
(expand-file-name "journal" denote-directory))
;; Default keyword for new journal entries. It can also be a list of
;; strings.
(setq denote-journal-keyword "journal")
;; Read the doc string of `denote-journal-title-format'.
(setq denote-journal-title-format 'day-date-month-year))
Footer
;; ; Now providing gleenote...
(provide 'gleemacs-denote)
;;; gleemacs-denote.el ends here
gleemacs-dired
Header
;;; gleemacs-dired.el --- Dired config for gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
dired
;; ; Dired
(use-package dired
:ensure nil
:commands (dired)
:config
(setq dired-recursive-copies 'always)
(setq dired-recursive-deletes 'always)
(setq delete-by-moving-to-trash t)
(setq dired-listing-switches
"-AGFhlv --group-directories-first --time-style=long-iso")
;; Constrain vertical cursor movement to lines within the buffer
(setq dired-movement-style 'bounded-files)
(add-hook 'after-init-hook #'window-divider-mode)
;; Stop direds "Omitting..." noise clogging up `*Messages*'
(setq dired-omit-verbose nil)
(add-hook 'dired-mode-hook #'dired-omit-mode)
(add-hook 'dired-mode-hook #'hl-line-mode)
(add-hook 'dired-mode-hook #'diff-hl-dired-mode)
(setq dired-auto-revert-buffer #'dired-directory-changed-p)
(setq dired-make-directory-clickable t)
(setq dired-free-space nil)
(setq dired-dwim-target t))
dired-subtree
;; This let's us `<TAB>' to expand a directory and list it's nested
;; contents as a subtree.
(use-package dired-subtree
:ensure t
:after dired
:bind
( :map dired-mode-map
("<tab>" . dired-subtree-toggle)
("TAB" . dired-subtree-toggle)
("<backtab>" . dired-subtree-remove)
("S-TAB" . dired-subtree-remove))
:config
(setq dired-subtree-use-backgrounds nil))
Nerd icons for dired
;; ; Prettyyyyyyyy
(use-package nerd-icons-dired
:hook
(dired-mode . nerd-icons-dired-mode)
:diminish nerd-icons-dired-mode)
Footer
;; ; Now providing glee...
(provide 'gleemacs-dired)
;;; gleemacs-dired.el ends here
gleemacs-eglot
Header
;;; gleemacs-eglot.el --- Eglot setup for gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
eglot and basedpyright
;; Set up the Language Server Protocol (LSP) servers using Eglot.
(use-package eglot
:hook ((python-mode python-ts-mode) . eglot-ensure)
:config
(setq eglot-autoshutdown 1)
(add-to-list 'eglot-server-programs
'((python-mode python-ts-mode)
. ("basedpyright-langserver" "--stdio")))
(setq-default eglot-workspace-configuration
'(:basedpyright (:typeCheckingMode "recommended"
:reportMissingImports t
:reportUnusedVariable t
:venvPath ".venv"
:venv ".venv"
:pythonPath "bin/python"))))
consult-eglot
(use-package consult-eglot
:after eglot)
eldoc
;; Setup eldoc
(use-package eldoc
:ensure nil
:hook ((eglot-managed-mode . eldoc-mode))
:diminish eldoc-mode
:custom
(eldoc-echo-area-use-multiline-p 2)
(eldoc-idle-delay 3.0)
:config
(with-eval-after-load 'eldoc-box
(diminish 'eldoc-box-hover-at-point-mode)))
eldoc-box
;; Setup eldoc in-frame popups
(use-package eldoc-box
:ensure t
:after eglot
:hook (eglot-managed-mode . eldoc-box-hover-at-point-mode)
:custom
;; tweak delay before popup
(eldoc-box-idle-delay 2)
;; move popups above the cursor for readability
(eldoc-box-hover-display-frame-above-point t)
;; customise hide borders
(eldoc-box-clear-frame-parameters '((internal-border-width . 6)
(left-fringe . 8)
(right-fringe . 8))))
Taming eglot hints
;; uncomment this if hints become too noisy
;; (setq eglot-ignored-server-capabilities '(:inlayHintProvider))
(setq eglot-send-changes-idle-time 0.3)
Footer
;; ; Now providing gleeglot...
(provide 'gleemacs-eglot)
;;; gleemacs-eglot.el ends here
gleemacs-email
Header
;;; gleemacs-email.el --- Gleemail -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;;; Commentary:
;;; Code:
message
(use-package message
:ensure nil
:defer t
:hook
(message-setup . message-sort-headers)
:config
(setq mail-user-agent 'message-user-agent
message-mail-user-agent t) ; use `mail-user-agent'
(setq mail-header-separator "--text follows this line--")
(setq message-elide-ellipsis "\n> [... %l lines elided]\n")
(setq compose-mail-user-agent-warnings nil)
(setq message-citation-line-function #'message-insert-formatted-citation-line)
(setq message-citation-line-format (concat "> From: %f\n"
"> Date: %a, %e %b %Y %T %z\n"
">")
message-ignored-cited-headers "") ; default is "." for all headers
(setq message-confirm-send t)
(setq message-kill-buffer-on-exit t)
;; (add-to-list 'mm-body-charset-encoding-alist '(utf-8 . base64))
(setq message-wide-reply-confirm-recipients nil))
smtpmail
(use-package smtpmail
:ensure nil
:after message
:config
;; Load gnutls library and add local cert to trustfiles
(require 'gnutls)
(add-to-list 'gnutls-trustfiles (expand-file-name "~/.cert/cert.pem"))
(setq send-mail-function #'smtpmail-send-it)
(setq smtpmail-smtp-server "127.0.0.1")
(setq smtpmail-smtp-service 1025)
(setq smtpmail-stream-type 'starttls))
notmuch
(require 'gleemacs-helpers)
(use-package notmuch
:ensure t
:defer t
:commands (notmuch notmuch-mua-new-mail notmuch-hello-mode)
:config
(let ((private (gleemacs-helper-auth-get-field "private" :user))
(public (gleemacs-helper-auth-get-field "public" :user)))
(setq notmuch-identities
(mapcar (lambda (str)
(format "%s <%s>" user-full-name str))
(list private public))
notmuch-fcc-dirs
`((,private . "private/Sent")
(,public . "public/Sent"))))
(setq notmuch-tree-unthreaded t)
(setq notmuch-search-oldest-first nil)
(setq notmuch-saved-searches
'((:name "inbox"
:query "tag:inbox"
:sort-order newest-first
:search-type unthreaded
:key "i")
(:name "unread"
:query "tag:unread"
:sort-order newest-first
:search-type unthreaded
:key "u")
(:name "flagged"
:query "tag:flagged"
:sort-order newest-first
:search-type unthreaded
:key "f")
(:name "sent"
:query "tag:replied"
:sort-order newest-first
:search-type unthreaded
:key "r")
(:name "drafts"
:query "tag:draft"
:sort-order newest-first
:search-type unthreaded
:key "d")
(:name "all mail"
:query "*"
:sort-order newest-first
:search-type unthreaded
:key "a")))
;; Remove certain emails from inbox
(setq notmuch-message-replied-tags '("+replied" "-inbox"))
(setq notmuch-message-forwarded-tags '("+forwarded" "-inbox"))
(setq notmuch-draft-tags '("+draft" "-inbox")))
Footer
;; ; Now providing gleemail...
(provide 'gleemacs-email)
;;; gleemacs-email.el ends here
gleemacs-essentials
Header
;;; gleemacs-essentials.el --- Essentials for Glee! -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;; The essentials are designed to be a base sit of modifications
;; and packages that aim to provide sane defaults for persistency
;; and practical aesthetics.
;;; Code:
emacs settings
;;;; `emacs' essential configuration
;; Courtesy of Protesilaos Stavrou
(use-package emacs
:ensure nil
:demand t
:config
;; General settings and common custom functions
(setq blink-matching-paren nil)
(setq custom-unlispify-tag-names nil)
(setq delete-pair-blink-delay 0.1) ; Emacs28
(setq delete-pair-push-mark t) ; Emacs 31
(setq echo-keystrokes-help nil) ; Emacs 30
(setq epa-keys-select-method 'minibuffer) ; Emacs 30
(setq eval-expression-print-length nil)
(setq find-library-include-other-files nil) ; Emacs 29
(setq help-window-select t)
(setq kill-do-not-save-duplicates t)
(setq mode-require-final-newline 'visit-save)
(setq next-error-recenter '(4)) ; center of the window
(setq remote-file-name-inhibit-auto-save t) ; Emacs 30
(setq remote-file-name-inhibit-delete-by-moving-to-trash t) ; Emacs 30
(setq save-interprogram-paste-before-kill t)
(setq scroll-error-top-bottom t)
(setq tramp-connection-timeout (* 60 10)) ; seconds
(setq truncate-partial-width-windows nil)
;; emacs asks confirm before exiting
(setq confirm-kill-emacs 'y-or-n-p))
diminish
;;;; `diminish'
;; Would you mind not being so loud down there?!
(use-package diminish
:config
(diminish 'visual-line-mode))
recentf
Maintains a list of recently opened files. Convenient when you need to jump back in later!
;;;; Recentf Config
(use-package recentf
:ensure nil
:hook (after-init . recentf-mode)
:config
(setq recentf-max-menu-items 1000)
(setq recentf-max-saved-items 100))
savehist
;;;; `savehist' (minibuffer and related histories)
(use-package savehist
:ensure nil
:hook (after-init . savehist-mode)
:config
(setq savehist-file (expand-file-name "savehist" no-littering-var-directory))
(setq history-length 100)
(setq history-delete-duplicates t)
(setq savehist-save-minibuffer-history t)
(dolist (gleemacs-savehist-vars '(kill-ring
mark-ring global-mark-ring
search-ring regexp-search-ring))
(add-to-list 'savehist-additional-variables gleemacs-savehist-vars)))
save-place
;;;; This helped me find my keys once!
(use-package save-place
:ensure nil
:hook (after-init . save-place-mode)
:config
(setq save-place-file (expand-file-name "save-place" no-littering-var-directory)))
mouse
;;;; Mouse and mouse wheel behaviour
(use-package mouse
:ensure nil
:hook (after-init . mouse-wheel-mode)
:config
(setq mouse-autoselect-window t) ; complements the auto-selection of my tiling window manager
(setq focus-follows-mouse t)
;; In Emacs 27+, use Control + mouse wheel to scale text.
(setq mouse-wheel-scroll-amount
'(1
((shift) . 5)
((meta) . 0.5)
((control) . text-scale))
mouse-drag-copy-region nil
make-pointer-invisible t
mouse-wheel-progressive-speed t
mouse-wheel-follow-mouse t)
;; Scrolling behaviour
(setq scroll-preserve-screen-position t
scroll-conservatively 1 ; affects `scroll-step'
scroll-margin 0
next-screen-context-lines 0))
autorevert
;; ; Automatically update buffers when modified in the background
;;;; Auto revert mode
(use-package autorevert
:ensure nil
:hook (after-init . global-auto-revert-mode)
:config
(setq auto-revert-verbose t))
delsel
;;;; Delete selection
(use-package delsel
:ensure nil
:hook (after-init . delete-selection-mode))
tooltip
;;;; Tooltips (tooltip-mode)
(use-package tooltip
:ensure nil
:hook (after-init . tooltip-mode)
:config
(setq tooltip-delay 0.5
tooltip-short-delay 0.5
tooltip-frame-parameters
'((name . "tooltip")
(internal-border-width . 10)
(border-width . 0)
(no-special-glyphs . t))))
no-littering
This nifty package redirects persistent data to /var/ and package
configurations to /etc/, nested in the <user-emacs-directory>. Where
packages require disk useage, they (should) have been configured to utilise the
no-littering-var-directory or no-littering-etc-directory as necessary.
In addition to cleanliness this should also help to ensure that our configuration is portable across systems, while minimising the chances of exposing sensitive data.
;;;; `no-literring'
;; ; Cleaning up those emacs streets
;; A great package the redirects package configuration files and persitent
;; data to the emacs directory's /etc and /var folders.
(use-package no-littering
:ensure t
:config
;; Prevent recentf junk
(with-eval-after-load 'recentf
(add-to-list 'recentf-exclude
(recentf-expand-file-name no-littering-var-directory))
(add-to-list 'recentf-exclude
(recentf-expand-file-name no-littering-etc-directory))))
Send backup files to no-littering
Persistence requires writing data to disk, and by default Emacs thinks any
working directory is it’s oyster, leading to a lot of stray #files# and
other-files~, and a cluttered .emacs.d folder. To keep our files tidy,
organised, and safe, we send these files to the respective no-littering-var
directory.
(let ((backup-dir (no-littering-expand-var-file-name "backups/"))
(autosave-dir (no-littering-expand-var-file-name "autosave/")))
;; Ensure directories exist
(make-directory backup-dir t)
(make-directory autosave-dir t)
;; Enable backups
(setq make-backup-files t)
;; Redirect all backups for `foo.bar~'
(setq backup-directory-alist `(("." . ,backup-dir)))
;; Numbered backups
(setq version-control t
kept-new-versions 10
kept-old-versions 2
delete-old-versions t)
;; Redirect auto-save files `#foo.bar#'
(setq auto-save-file-name-transforms
`((".*" ,(concat autosave-dir "\\1") t)))
;; Redirect auto-save lists `.saves-*'
(setq auto-save-list-file-prefix
(no-littering-expand-var-file-name "autosave/saves-")))
undo-fu
;; ; Undo fu and session persistence
;; The undo-fu package is a lightweight wrapper around Emacs' built-in undo
;; system, providing more convenient undo/redo functionality.
(use-package undo-fu
:ensure t
:commands (undo-fu-only-undo
undo-fu-only-redo
undo-fu-only-redo-all
undo-fu-disable-checkpoint))
undo-fu-session
;; The undo-fu-session package complements undo-fu by enabling the saving
;; and restoration of undo history across Emacs sessions, even after restarting.
(use-package undo-fu-session
:ensure t
:commands undo-fu-session-global-mode
:hook (after-init . undo-fu-session-global-mode)
:custom
(undo-fu-session-directory
(expand-file-name "undo-fu/" no-littering-var-directory))
(undo-fu-session-linear t))
server
;;;; Emacs `server' (allow `emacsclient' to connect to running session)
;; ; Enable server mode, but only once!
(use-package server
:ensure nil
:defer 1
:config
(setq server-client-instructions nil)
(unless (or (server-running-p) (daemonp))
(server-start)))
keyboard-quit DWIM
;; DWIM C-g
;; Courtesy of Protesilaos
(defun prot/keyboard-quit-dwim ()
"Do-What-I-Mean behaviour for a general `keyboard-quit'.
The generic `keyboard-quit' does not do the expected thing when
the minibuffer is open. Whereas we want it to close the
minibuffer, even without explicitly focusing it.
The DWIM behaviour of this command is as follows:
- When the region is active, disable it.
- When a minibuffer is open, but not focused, close the minibuffer.
- When the Completions buffer is selected, close it.
- In every other case use the regular `keyboard-quit'."
(interactive)
(cond
((region-active-p)
(keyboard-quit))
((derived-mode-p 'completion-list-mode)
(delete-completion-window))
((> (minibuffer-depth) 0)
(abort-recursive-edit))
(t
(keyboard-quit))))
(global-set-key [remap keyboard-quit] 'prot/keyboard-quit-dwim)
nerd-icons
(use-package nerd-icons
:defer 1
:custom
(setq nerd-icons-scale-factor 1.1)
;; The Nerd Font you want to use in GUI
;; "Symbols Nerd Font Mono" is the default and is recommended
;; but you can use any other Nerd Font if you want
;; (nerd-icons-font-family "Symbols Nerd Font Mono")
)
dashboard
;; ; Simple dashboard
;; I rarely use the scratch, so lets add some utility at startup!
(use-package dashboard
:ensure t
:config
(setq dashboard-projects-backend 'project-el)
(setq dashboard-items '((recents . 5)
(projects . 5)
(bookmarks . 5)))
;; (registers . 5)))
(setq dashboard-buffer-name "Dashboard")
(setq dashboard-startupify-list '(dashboard-insert-newline
dashboard-insert-banner
dashboard-insert-newline
dashboard-insert-banner-title
dashboard-insert-newline
dashboard-insert-items
dashboard-insert-init-info
dashboard-insert-newline
dashboard-insert-footer))
(dashboard-setup-startup-hook))
(setq initial-buffer-choice (lambda () (get-buffer-create dashboard-buffer-name)))
ibuffer improvements
;; ; IBuffer niceties
;; This setup courtesy of Steve Purcell
;; https://github.com/purcell/emacs.d/blob/master/lisp/init-ibuffer.el
;; In short, when we C-x C-b our IBuffer display is grouped by project
(use-package ibuffer-vc
:ensure t
:hook (ibuffer . ibuffer-set-up-preferred-filters)
:init
(defun ibuffer-set-up-preferred-filters ()
(ibuffer-vc-set-filter-groups-by-vc-root)
(unless (eq ibuffer-sorting-mode 'filename/process)
(ibuffer-do-sort-by-filename/process)))
:config
(setq-default ibuffer-show-empty-filter-groups nil)
(setq ibuffer-filter-group-name-face 'font-lock-doc-face)
(setq ibuffer-formats
'((mark modified read-only vc-status-mini " "
(icon 2 2 :left)
(name 22 22 :left :elide)
" "
(size-h 9 -1 :right)
" "
(mode 12 12 :left :elide)
" "
vc-relative-file)
(mark modified read-only vc-status-mini " "
(icon 2 2 :left)
(name 22 22 :left :elide)
" "
(size-h 9 -1 :right)
" "
(mode 14 14 :left :elide)
" "
(vc-status 12 12 :left)
" "
vc-relative-file)))
;; after ibuffer loads
(with-eval-after-load 'ibuffer
(define-ibuffer-column size-h
(:name "Size" :inline t)
(file-size-human-readable (buffer-size)))))
Replace list-buffers with ibuffer
(global-set-key [remap list-buffers] 'ibuffer)
Nerd icons for ibuffer
;; ; ibuffer nerd-icons
;; Thanks to Prot for mentioning this one. Icons are extremely useful for
;; differentiating buffers when you have metric tonnes of them open.
(use-package nerd-icons-ibuffer
:ensure t
:defer t
:hook (ibuffer-mode . nerd-icons-ibuffer-mode)
:config
(setq nerd-icons-ibuffer-icon t))
Footer
;; Now providing the gleessentials!
(provide 'gleemacs-essentials)
;;; gleemacs-essentials.el ends here
gleemacs-flymake
Header
;;; gleemacs-flymake.el --- Flymake Goodness! -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
flymake
;;;; `flymake'
(use-package flymake
:ensure t
:hook (prog-mode . flymake-mode)
:config
(setq flymake-suppress-zero-counters t))
(defun gleemacs/flymake-eldoc-setup ()
(add-hook 'eldoc-documentation-functions
#'flymake-eldoc-function
nil t))
flymake-flycheck
;;;; `flycheck'
(use-package flymake-flycheck
:after flymake
:hook ((flymake-mode-hook . flymake-flycheck.auto)
(prog-mode-hook . flymake-mode)
(flymake-mode . gleemacs/flymake-eldoc-setup)))
Footer
;; ; Now providing glee...
(provide 'gleemacs-flymake)
;;; gleemacs-flymake.el ends here
gleemacs-git
Header
;;; gleemacs-git.el --- Git and Projects for gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
project
;; ; Projects Setup
;;;; `project'
(use-package project
:ensure nil
:bind
(("C-x p ." . project-dired)
("C-x p C-g" . keyboard-quit)
("C-x p <return>" . project-dired)
("C-x p <delete>" . project-forget-project))
:config
(setopt project-switch-commands
'((project-find-file "Find file")
(project-find-regexp "Find regexp")
(project-find-dir "Find directory")
(project-dired "Root dired")
(project-vc-dir "VC-Dir")
(project-shell "Shell")
(keyboard-quit "Quit")))
(setq project-vc-extra-root-markers '(".project")) ; Emacs 29
(setq project-key-prompt-style t)) ; Emacs 30
transient
(use-package transient
:defer t
:config
(setq transient-show-popup 0.5))
magit
;;; Interactive and powerful git front-end (Magit)
(use-package magit
:ensure t
:bind
( :map global-map
("C-c g g" . magit-status)
:map magit-mode-map
("C-w" . nil)
("M-w" . nil))
:init
(setq magit-define-global-key-bindings nil)
;; (setq magit-section-visibility-indicator '(magit-fringe-bitmap . magit-fringe-bitmap))
:config
(setq git-commit-summary-max-length 50)
;; NOTE 2023-01-24: I used to also include `overlong-summary-line'
;; in this list, but I realised I do not need it. My summaries are
;; always in check. When I exceed the limit, it is for a good
;; reason.
(setq git-commit-style-convention-checks '(non-empty-second-line))
(setq magit-diff-refine-hunk t)
;; Show icons for files in the Magit status and other buffers.
(with-eval-after-load 'magit
(setq magit-format-file-function #'magit-format-file-nerd-icons)))
magit-repos
(use-package magit-repos
:ensure nil ; part of `magit'
:commands (magit-list-repositories)
:init
(setq magit-repository-directories
'(("~/GitHub/" . 2)))
;; Make escape quit magit prompts
:config (define-key transient-map (kbd "<escape>") 'transient-quit-one))
diff-hl
;; Diff-hl
(use-package diff-hl
:hook ((dired-mode . diff-hl-dired-mode-unless-remote)
(magit-post-refresh . diff-hl-magit-post-refresh))
:init (global-diff-hl-mode))
Footer
;; ; Now providing gleet..
(provide 'gleemacs-git)
;;; gleemacs-git.el ends here
gleemacs-helpers
Header
;;; gleemacs-helpers.el --- Friendly Helpers -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;;;
;;; Commentary:
;;
;; Just a bunch of helpful helpers gleefully helping.
;;
;;;
;;; Code:
gleemacs-helper-auth-get-field
;; This useful function is courtesy of Protesilaos Stavrou, and allows for
;; secure retrieval of key-value pairs from `.authinfo.gpg' files.
;; See: https://protesilaos.com/emacs/dotemacs#h:24f17614-ee3d-4cd1-bbe3-f53e4f39c281
;; Useage: email (gleemacs-helper-auth-get-field "public" :user)
;; (setq user-mail-address email)
;;;###autoload
(defun gleemacs-helper-auth-get-field (host prop)
"Find PROP in `auth-sources' for HOST entry."
(require 'auth-source)
(when-let* ((source (auth-source-search :host host)))
(if (eq prop :secret)
(funcall (plist-get (car source) prop))
(plist-get (flatten-list source) prop))))
Footer
;; ; Now providing gleeful helpers...
(provide 'gleemacs-helpers)
;;; gleemacs-helpers.el ends here
gleemacs-irc
Header
;;; gleemacs-irc.el --- Gleemacs IRC -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;; sTfU oR pEoN
;;; Code:
erc setup
;;;; `erc'
(use-package erc
:ensure nil
:config
(setq erc-track-exclude-types '("JOIN" "PART" "QUIT" "NICK" "MODE"))
(setq erc-prompt-for-nickserv-password nil
erc-user-full-name "Sam Sinclair"
erc-use-tls t)
(setq erc-autojoin-channels-alist
'(("Libera.Chat" "#emacs" "#fsf" "#linux" "#linux-aus" "#archlinux" "#archlinux-aur")))
)
erc-libera-connect
(require 'gleemacs-helpers)
;; See `gleemacs-keybindings' for usage
(defun gleemacs/erc-libera-connect ()
"Connect to Libera.Chat using ERC and authinfo credentials."
(interactive)
(let ((nick (gleemacs-helper-auth-get-field "irc.libera.chat" :user)))
(erc-tls :server "irc.libera.chat"
:port 6697
:nick nick)))
Footer
(provide 'gleemacs-irc)
;;; gleemacs-irc.el ends here
gleemacs-keybindings
Header
;;; gleemacs-keybindings.el --- Gleemacs Keybindings -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;; A centralised place to organise your bindings.
;;; Code:
override and adjust emacs defaults
;;; ; Emacs Defaults
;; Keys I unbind here are either to avoid accidents or to bind them
;; elsewhere later in the configuration.
(use-package emacs
:bind
( :map global-map
("C-z" . nil) ; I have a window manager, thanks!
("C-x C-z" . nil) ; same idea as above
("C-x C-c" . nil) ; avoid accidentally exiting Emacs
("C-x C-r" . recentf-open) ; override `find-file-read-only'
("C-h h" . nil) ; Never show that "hello" file
("M-`" . nil)
("C-h K" . describe-keymap) ; overrides `Info-goto-emacs-key-command-node'
("C-h u" . apropos-user-option)
("C-h F" . apropos-function) ; lower case is `describe-function'
("C-h V" . apropos-variable) ; lower case is `describe-variable'
("C-h L" . apropos-library) ; lower case is `view-lossage'
("C-h c" . describe-char))) ; overrides `describe-key-briefly'
org
;;; ; Org Mode niceties
(use-package org
:bind
( :map org-mode-map
("C-M-<return>" . org-insert-subheading)
("M-S-<return>" . org-insert-todo-subheading)))
general
Utilising a centralised module for our bindings effectively presents an interface to our workspace in one area. Therefore, we always know where to look to get a birds-eye view of our mappings. This approach (in theory) also minimises error in terms of overwriting existing bindings, which can be easy to do when they’re spread around. The downside being a duplication of efforts in removing packages and their bindings (if any) from the aforementioned module, but this shouldn’t happen often.
My reasoning for using general was a matter of speed of deployment and ease of
configuration in the outset, and because leader keys have bosses too. I
understand this adds unnecessary bloat (as it turns out, general is not just
for mapping bindings, salute!), so I’d like to roll my own functionality later.
;;; ; AttennnnnnnNTION!
(use-package general
:ensure t
:config
;; Define the leader key
(general-create-definer my/leader
:prefix "C-c")
;; Global bindings
(my/leader
;; Reserving this for flymake and eglot
;; "c" '(:ignore t :which-key "code")
"f" '(:ignore t :which-key "files")
"ff" '(find-file :which-key "find file")
"fr" '(recentf-open-files :which-key "recent files")
"b" '(:ignore t :which-key "buffers")
"bb" '(switch-to-buffer :which-key "switch buffer")
"bk" '(kill-buffer :which-key "kill buffer")
;; Insert
"i" '(:ignore t :which-key "insert")
"is" '(clipboard-kill-ring-save :which-key "save to clipboard")
"ip" '(mouse-yank-primary :which-key "yank clipboard pop")
;; Open
"o" '(:ignore t :which-key "open")
"oe" '(notmuch :which-key "email")
"oc" '(gleemacs/erc-libera-connect :which-key "erc")
"or" '(elfeed :which-key "elfeed")
"ot" '(vterm :which-key "vterm")
;; ; Notes
"n" '(:ignore t :which-key "notes")
"na" '(org-agenda :which-key "org-agenda")
"nb" '(denote-backlinks :whick-key "denote-backlinks")
"nn" '(denote :which-key "denote-create-note")
"no" '(denote-open-or-create :which-key "denote-find-notes")
"nf" '(denote-grep :which-key "denote-grep")
"nr" '(denote-dired :which-key "denote-regex")
"nj" '(denote-journal-new-entry :which-key "denote-journal")
"nl" '(denote-link-or-create :which-key "denote-insert-link")
"nL" '(denote-add-links :which-key "denote-insert-links")
"nx" '(org-capture :which-key "org-capture")
"nr" '(denote-rename-file :which-key "denote rename note")
"nR" '(denote-rename-file-using-front-matter "denote rename w front matter")
"ns" '(org-search-view :which-key "org-search-notes")
"nt" '(org-todo-list :which-key "todo list")
;; search
"s" '(:ignore t :which-key "search")
"sb" '(consult-line :which-key "buffer")
"sb" '(consult-line-multi 'all-buffers :which-key "all buffers")
"sd" '(dictionary-search :which-key "dictionary-search")
"w" '(:ignore t :which-key "windows")
"we" '(balance-windows :which-key "balance windows")
"wm" '(maximize-window :which-key "maximise window")
"wd" '(delete-window :which-key "delete window")
"g" '(:ignore t :which-key "git")
"gg" '(magit-status :which-key "status")
"gc" '(magit-commit :which-key "commit")
"C-q" '(save-buffers-kill-terminal :which-key "quit emacs")))
Footer
;; ; Now providing gleebindings...
(provide 'gleemacs-keybindings)
;;; gleemacs-keybindings.el ends here
gleemacs-markdown
Header
;;; gleemacs-markdown.el --- Markdown in gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
markdown-mode
(use-package markdown-mode
:ensure t
:mode ("\\.md\\'" . markdown-mode)
:init
(setq markdown-command "pandoc"))
markdown-toc
(use-package markdown-toc
:after markdown-mode
:commands (markdown-toc-generate-toc markdown-toc-refresh-toc))
Footer
;;; Now providing glee...
(provide 'gleemacs-markdown)
;;; gleemacs-markdown.el ends here
gleemacs-languages
Header
;;; gleemacs-org.el --- Langauges in gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
treesit
;; Setup our grammar sources list
(setq treesit-language-source-alist
'((python "https://github.com/tree-sitter/tree-sitter-python")
(c "https://github.com/tree-sitter/tree-sitter-c")
(bash "https://github.com/tree-sitter/tree-sitter-bash")
(json "https://github.com/tree-sitter/tree-sitter-json")
(html "https://github.com/tree-sitter/tree-sitter-html")
(css "https://github.com/tree-sitter/tree-sitter-css")
(toml "https://github.com/tree-sitter/tree-sitter-toml")
(make "https://github.com/tree-sitter-grammars/tree-sitter-make")))
;; Map major modes to a treesit mode
(setq major-mode-remap-alist
'(;(python-mode . python-ts-mode)
(css-mode . css-ts-mode)
(bash-mode . bash-ts-mode)
(json-mode . json-ts-mode)
(sh-mode . bash-ts-mode)))
text-mode
These settings are also inherited by other text-mode modes such as org and markdown.
(use-package text-mode
:ensure nil
:mode "\\`\\(README\\|CHANGELOG\\|COPYING\\|LICENSE\\)\\'"
:hook
((text-mode . turn-on-auto-fill)
(prog-mode . (lambda () (setq-local sentence-end-double-space t))))
:config
(setq sentence-end-double-space nil)
(setq sentence-end-without-period nil)
(setq colon-double-space nil)
(setq use-hard-newlines nil)
(setq adaptive-fill-mode t))
Settings for common file types
PKGBUILD
;;;; Arch Linux packaging scripts (sh-mode)
(use-package sh-script
:ensure nil
:mode ("PKGBUILD" . sh-mode))
systemd and .conf files
;;;; SystemD and other configuration files (conf-mode)
(use-package conf-mode
:ensure nil
:mode ("\\`dircolors\\'" "\\.\\(service\\|timer\\)\\'"))
Footer
;; ; Now providing glee...
(provide 'gleemacs-languages)
;;; gleemacs-org.el ends here
gleemacs-org
Header
;;; gleemacs-org.el --- Org for gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
australia-holidays
;; ; Set up calendar
;; Straya holidays c/o jmibanez
(use-package australia-holidays
:ensure t)
Add daylight savings time
(with-eval-after-load 'australia-holidays
(setq australia-holidays-for-vic
(append australia-holidays-for-vic
'((holiday-float 10 0 1 "Daylight Savings starts (forward 1 hour @ 02:00)")
(holiday-float 4 0 1 "Daylight Savings ends (back 1 hour @ 03:00)")))))
calendar
(use-package calendar
:ensure nil
:commands (calendar)
:config
(setq calendar-mark-diary-entries-flag nil)
(setq calendar-mark-holidays-flag t)
(setq calendar-mode-line-format nil)
(setq calendar-time-display-form
'( 24-hours ":" minutes
(when time-zone (format "(%s)" time-zone))))
(setq calendar-week-start-day 1) ; Monday
(setq calendar-date-style 'iso)
(setq calendar-time-zone-style 'numeric) ; Emacs 28.1
(setq calendar-holidays (append australia-holidays-for-vic))
(require 'cal-dst)
(setq calendar-standard-time-zone-name "+1000")
(setq calendar-daylight-time-zone-name "+1100"))
org
;; ; Org Mode
;; Organised Relatively Gracefully
(use-package org
:ensure nil
:init
(setq org-directory (expand-file-name "~/org/"))
(setq org-default-notes-file (expand-file-name "notes.org" org-directory))
(setq org-agenda-files
'("notes.org"
"projects.org"
"todo.org"))
(setq org-imenu-depth 7)
(add-to-list 'safe-local-variable-values '(org-hide-leading-stars . t))
(add-to-list 'safe-local-variable-values '(org-hide-macro-markers . t))
Org general settings
:config
;;;; general settings
(setq org-ellipsis " [...]")
(setq org-M-RET-may-split-line '((default . nil)))
(setq org-hide-emphasis-markers nil)
(setq org-hide-leading-stars nil)
(setq org-cycle-separator-lines 0)
(setq org-fold-catch-invisible-edits 'show)
(setq org-loop-over-headlines-in-active-region 'start-level)
(setq org-read-date-prefer-future 'time)
(setq org-highlight-latex-and-related nil) ; other options affect elisp regexp in src blocks
(setq org-fontify-quote-and-verse-blocks t)
(setq org-fontify-whole-block-delimiter-line t)
(setq org-track-ordered-property-with-tag t)
(setq org-highest-priority ?A)
(setq org-lowest-priority ?C)
(setq org-default-priority ?A)
(setq org-startup-indented t)
;; No blank lines before new entries
(setq org-blank-before-new-entry
'((heading . nil)
(plain-list-item . nil)))
Org indentation settings
;; Indentation settings
(setq org-insert-heading-respect-content t)
(setq org-adapt-indentation nil)
(setq org-indentation-per-level 4)
Org todo and refile settings
;; Todo and Refile settings
;; The evil do when I do todos
(setq org-todo-keywords
'((sequence "TODO(t)" "MAYBE(m)" "|" "DONE(d!)" "CANCELED(c@)")))
(setq org-refile-targets
'((org-agenda-files :maxlevel . 3)
(my/org-files-list :maxlevel . 3)
(nil :maxlevel . 3)))
(setq org-refile-use-outline-path 'file
org-outline-path-complete-in-steps nil
org-refile-allow-creating-parent-nodes 'confirm)
;; Time and State Logging
(setq org-log-done 'time)
(setq org-log-into-drawer t)
(setq org-log-note-clock-out nil)
(setq org-log-redeadline 'time)
(setq org-log-reschedule 'time)
Org links
;; Links
(setq org-return-follows-link t)
(setq org-link-context-for-files t)
(setq org-link-keep-stored-after-insertion nil)
(setq org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
Org codeblock settings
;; Codeblocks
(setq org-confirm-babel-evaluate nil)
(setq org-src-window-setup 'current-window)
(setq org-edit-src-persistent-message nil)
(setq org-src-fontify-natively t)
(setq org-src-preserve-indentation t)
(setq org-src-tab-acts-natively t)
(setq org-edit-src-content-indentation 0)
Org export settings
;; Export
(setq org-export-with-toc t)
(setq org-export-headline-levels 8)
(setq org-export-dispatch-use-expert-ui nil)
(setq org-html-htmlize-output-type nil)
(setq org-html-head-include-default-style nil)
(setq org-html-head-include-scripts nil)
Setup project-based note taking
;; project helpers
(defun my/project-name ()
"Return current project name or error."
(let* ((proj (project-current))
(root (and proj (project-root proj))))
(if root
(file-name-nondirectory (directory-file-name root))
(user-error "Not in a project"))))
;; central target helper
(defun my/org--central-file (file project)
"Visit FILE under `org-directory` and ensure PROJECT tree exists.
Leaves point at the subtree and returns the current buffer."
(let ((path (expand-file-name file org-directory)))
(set-buffer (org-capture-target-buffer path))
(widen)
(goto-char (point-min))
(my/org--ensure-heading
(delq nil
(append (org-capture-get :parents)
(list project (org-capture-get :heading)))))
(current-buffer)))
;; wrappers for org-capture
(defun my/org-capture-central-project-todo-target ()
(my/org--central-file "projects.org" (my/project-name)))
(defun my/org-capture-central-project-notes-target ()
(my/org--central-file "projects.org" (my/project-name)))
(defun my/org-capture-central-project-changelog-target ()
(my/org--central-file "projects.org" (my/project-name)))
;; Project-aware Org capture templates
(defvar my/org-capture-todo-file "todo.org")
(defvar my/org-capture-notes-file "notes.org")
(defvar my/org-capture-changelog-file "changelog.org")
(defvar my/org-capture-projects-file "projects.org")
(defun my/project-root ()
;; "Return the current project root or signal an error."
(or (and (fboundp 'project-root)
(ignore-errors (project-root)))
(and (fboundp 'project-current)
(when-let ((proj (project-current))) (project-root proj)))
(user-error "Not in a project")))
(defun my/org--local-root (filename)
;; "Find FILENAME in the nearest parent directory or project root."
(let ((path (locate-dominating-file default-directory filename)))
(expand-file-name filename (or path (my/project-root)))))
(defun my/org-capture-project-todo-file ()
(my/org--local-root my/org-capture-todo-file))
(defun my/org-capture-project-notes-file ()
(my/org--local-root my/org-capture-notes-file))
(defun my/org-capture-project-changelog-file ()
(my/org--local-root my/org-capture-changelog-file))
(defun my/org--ensure-heading (headings &optional level)
"Ensure nested HEADINGS exist up to LEVEL in the current buffer."
(setq level (or level 1))
(if (not headings)
(widen)
(if (and (re-search-forward
(format org-complex-heading-regexp-format
(regexp-quote (car headings))) nil t)
(= (org-current-level) level))
(progn (beginning-of-line) (org-narrow-to-subtree))
(goto-char (point-max))
(unless (bolp) (insert "\n"))
(insert (make-string level ?*) " " (car headings) "\n")
(beginning-of-line 0))
(my/org--ensure-heading (cdr headings) (1+ level))))
Setup capture templates
;; Personal
(setq org-capture-templates
`(("t" "Personal todo" entry
(file+headline ,(expand-file-name "todo.org" org-directory) "Inbox")
,(concat "* TODO %?\n%i"
":PROPERTIES:\n"
":CAPTURED: %U\n"
":CUSTOM_ID: h:%(format-time-string \"%Y%m%dT%H%M%S\")\n"
":END:\n\n"
"%a")
:empty-lines-after 1
:prepend t)
("n" "Personal note" entry
(file+headline ,(expand-file-name "notes.org" org-directory) "Inbox")
,(concat "* %u %?\n%i"
":PROPERTIES:\n"
":CAPTURED: %U\n"
":CUSTOM_ID: h:%(format-time-string \"%Y%m%dT%H%M%S\")\n"
":END:\n\n"
"%a")
:empty-lines-after 1
:prepend t)
("j" "Journal" entry
(file+olp+datetree ,(expand-file-name "journal.org" org-directory))
,(concat "* %U %?\n%i"
":PROPERTIES:\n"
":CAPTURED: %U\n"
":CUSTOM_ID: h:%(format-time-string \"%Y%m%dT%H%M%S\")\n"
":END:\n\n"
"%a")
:empty-lines-after 1
:prepend t)
;; Project Specific Templates
("p" "Templates for projects")
;;
("pt" "Project-local todo" entry ; {project-root}/todo.org
(file+headline (my/org-capture-project-todo-file) "Inbox")
"* TODO %?\n%i\n%a" :prepend t)
("pn" "Project-local notes" entry ; {project-root}/notes.org
(file+headline (my/org-capture-project-notes-file) "Inbox")
"* %U %?\n%i\n%a" :prepend t)
("pc" "Project-local changelog" entry ; {project-root}/changelog.org
(file+headline (my/org-capture-project-changelog-file) "Unreleased")
"* %U %?\n%i\n%a" :prepend t)
;; ;; Centralise Project Templates
("o" "Centralized project")
("ot" "Project todo" entry
(function my/org-capture-central-project-todo-target)
"* TODO %?\n%U\n%i\n%a"
:heading "Tasks")
("on" "Project notes" entry
(function my/org-capture-central-project-notes-target)
"* %U %?\n%i\n%a"
:heading "Notes" :prepend t)
("oc" "Project changelog" entry
(function my/org-capture-central-project-changelog-target)
"* %U %?\n%i\n%a"
:heading "Changelog" :prepend t))))
Custom function gleemacs/org-assign-custom-ids
(defun gleemacs/org-assign-custom-ids ()
"Assign CUSTOM_ID to all Org headings in the current buffer that lack one.
IDs formatted as h:<UUID>."
(interactive)
(org-map-entries
(lambda ()
(unless (org-entry-get nil "CUSTOM_ID")
(let ((id (concat "h:" (org-id-new))))
(org-entry-put nil "CUSTOM_ID" id))))))
org-superstar
;; Org Superstar
;; Make headings and lists nice :)
(use-package org-superstar
:ensure t
:after org
:hook (org-mode . org-superstar-mode)
:config
;; Set different bullets, with one getting a terminal fallback.
(setq org-superstar-headline-bullets-list
'("◉" ("🞛" ?◈) "○" "▷"))
;; Stop cycling bullets to emphasize hierarchy of headlines.
(setq org-superstar-cycle-headline-bullets nil)
;; Hide away leading stars on terminal.
(setq org-superstar-leading-fallback ?\s))
org-tempo
;; Org Tempo
;; Allows expansion of `<s' + TAB to expand a begin_src tag
(use-package org-tempo
:ensure nil
:after org)
ox-gfm
;; `ox-gfm'
;; GitHub flavoured markdown exporting for org
(use-package ox-gfm
:ensure t
:after org)
Footer
;; ; Now providing glee...
(provide 'gleemacs-org)
;;; gleemacs-org.el ends here
gleemacs-python
Header
;;; gleemacs-python.el --- Gleefull Python -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
General settings
;; I appreciate the guess, but do it quietly please :)
;; Silence guessing indent messages!
(setq python-indent-guess-indent-offset-verbose nil)
pyvenv
(use-package pyvenv
:ensure t
:hook ((python-mode python-ts-mode) . my/pyvenv-auto)
:config
(defun my/pyvenv-auto ()
"Auto-activate project .venv if present."
(let ((venv (locate-dominating-file default-directory ".venv")))
(when venv
(pyvenv-activate (expand-file-name ".venv" venv))))))
Footer
(provide 'gleemacs-python)
;;; gleemacs-python.el ends here
gleemacs-rss
Header
;;; gleemacs-rss.el --- RSS for gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;; Gleerss are on the house!
;;; Code:
elfeed
;; Elfeed
;; Get your favourite writings and other tidibits in plain text
(use-package elfeed
:ensure t
:hook
((elfeed-show-mode . visual-line-mode)
(elfeed-search-mode . elfeed-update))
:commands elfeed
:init
(setq elfeed-db-directory
(expand-file-name "elfeed/db" no-littering-var-directory)
elfeed-enclosure-default-dir
(expand-file-name "elfeed/enclosures" no-littering-var-directory))
:config
(setq elfeed-search-filter "@2-weeks-ago")
(setq elfeed-feeds
'("https://lwn.net/headlines/rss"
"https://sachachua.com/blog/feed/index.xml"
"https://protesilaos.com/master.xml"
"https://suckless.org/atom.xml"))
(make-directory elfeed-db-directory t))
elfeed-goodies setup
(use-package elfeed-goodies
:after elfeed
:config
(elfeed-goodies/setup))
Footer
;; ; Now providing the glee...
(provide 'gleemacs-rss)
;;; gleemacs-rss.el ends here
gleemacs-search
Header
;;; gleemacs-search.el --- Search setup for gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
isearch
;; ; `isearch'
;; Like the phone, but much better
(use-package isearch
:ensure nil
:demand t
:config
;; shows the position of current match releative to total count.
(setq isearch-lazy-count t)
(setq lazy-count-prefix-format "(%s/%s) ")
(setq lazy-count-suffix-format nil))
rg and ripgrep tooling
;;; grep and xref tooling
;; Courtesy of Protesilaos
(defvar gleemacs/ripgrep (or (executable-find "rg") (executable-find "ripgrep"))
"Store path to ripgrep executable, else nil.")
re-builder
(use-package re-builder
:ensure nil
:commands (re-builder regexp-builder)
:config
(setq reb-re-syntax 'read))
xref
(use-package xref
:ensure nil
:commands (xref-find-definitions xref-go-back)
:config
;; All those have been changed for Emacs 28
(setq xref-show-definitions-function #'xref-show-definitions-completing-read) ; for M-.
(setq xref-show-xrefs-function #'xref-show-definitions-buffer) ; for grep and the like
(setq xref-file-name-display 'project-relative)
(setq xref-search-program (if gleemacs/ripgrep 'ripgrep 'grep)))
grep
(use-package grep
:ensure nil
:commands (grep lgrep rgrep)
:config
(setq grep-save-buffers nil)
(setq grep-use-headings t) ; Emacs 30
(setq grep-program (or gleemacs/ripgrep (executable-find "grep")))
(setq grep-template
(if gleemacs/ripgrep
"/usr/bin/rg -nH --null -e <R> <F>"
"/usr/bin/grep <X> <C> -nH --null -e <R> <F>")))
Footer
;; ; Now providing glee...
(provide 'gleemacs-search)
;;; gleemacs-search.el ends here
gleemacs-spellcheck
Header
;;; gleemacs-spellcheck.el --- Spellcheck for gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
flyspell
;; ; spel chek fr emacs
(use-package flyspell
:ensure nil
:config
(setq flyspell-issue-message-flag nil)
(setq flyspell-issue-welcome-flag nil)
(setq ispell-local-dictionary "en_GB")
(setq ispell-dictionary "en_GB"))
dictionary
;; ; 8==D tionary
(use-package dictionary
:ensure nil
:config
(setq dictionary-server "dict.org"
dictionary-default-popup-strategy "lev" ; read doc string
dictionary-create-buttons nil
dictionary-use-single-buffer t))
altcaps
;; ; cOdInG iS dEaD bRo!1!
;; RTFM: <https://protesilaos.com/emacs/altcaps>.
;; "ALTCAPS Lets Trolls Convert Aphorisms to Proper Shitposts."
(use-package altcaps
:ensure t)
Footer
;; ; now provyding gle
(provide 'gleemacs-spellcheck)
;;; gleemacs-spellcheck.el ends here
gleemacs-terminal
Header
;;; gleemacs-terminal.el --- terminal? in gleemacs?! -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
vterm
;; ; vterm! the C based terminal emulator for emacs
;; https://github.com/akermu/emacs-libvterm
(use-package vterm
:ensure t)
Footer
;; Now providing gleetty...
(provide 'gleemacs-terminal)
;;; gleemacs-terminal.el ends here
gleemacs-window
Header
;;; gleemacs-window.el --- Window and buffer setup for gleemacs -*- no-byte-compile: t; no-native-compile: t; lexical-binding: t; -*-
;; Author: Sam Sinclair
;; URL: https://github.com/s6muel/gleemacs
;; Package-Requires: ((emacs "29.1"))
;; Version: 0.1.0
;; SPDX-License-Identifier: GPL-3.0-or-later
;;; Commentary:
;;; Code:
Setting up where certain buffers appear
Taming buffer the magic dragon.
This is a way to make sure certain buffers are displayed in a way that doesn’t inhibit our workspace. Thanks to Prot and his Emacs video on the subject.
;; Anatomy of a buffer alist entry
( BUFFER-MATCHER ; The buffer name/type
LIST-OF-DISPLAY-FUNCTIONS ; What we want to do with the buffer
&optional PARAMETERS)
(setq display-buffer-alist
'(("\\*Occur\\*"
(display-buffer-in-direction)
(direction . below)
(window-height . 0.3))
("\\`\\*Async Shell Command\\*\\'"
(display-buffer-no-window))
("\\`\\*\\(Warnings\\|Compile-Log\\|Org Links\\)\\*\\'"
(display-buffer-no-window)
(allow-no-window . t))
("\\*\\(Org \\(Select\\|Note\\)\\|Agenda Commands\\)\\*"
(display-buffer-in-side-window)
(dedicated . t)
(side . bottom)
(slot . 0)
(window-parameters . ((mode-line-format . none))))
((or . ((derived-mode . flymake-diagnostics-buffer-mode)
(derived-mode . flymake-project-diagnostics-mode)
(derived-mode . messages-buffer-mode)
(derived-mode . backtrace-mode)))
(display-buffer-reuse-mode-window display-buffer-at-bottom)
(mode . (flymake-diagnostics-buffer-mode
flymake-project-diagnostics-mode
messages-buffer-mode
backtrace-mode))
(window-height . 0.3)
(dedicated . t)
(preserve-size . (t . t))
(body-function . select-window))))
gleemacs modeline setup
;; ; Setup modeline helpers -----------------
;; Right align helper
(defun gleemacs/mode-line-rhs (segments)
"Align SEGMENTS to the right."
(let* ((txt (string-trim (format-mode-line segments)))
(w (max 1 (string-width txt))))
(list
(propertize " " 'display `((space :align-to (- right ,w))))
txt)))
;; Show a path for the buffer instead of just `buffer-file-name'
(defun gleemacs/mode-line-path ()
"Return a readable path for the current buffer."
(cond
((buffer-file-name)
(if-let* ((proj (project-current nil))
(root (project-root proj)))
;; Inside project: path relative to project root
(file-relative-name
(propertize buffer-file-name 'face 'bold)
root)
;; Outside project: ~/full/path
(abbreviate-file-name
(propertize buffer-file-name 'face 'bold))))
;; Buffers like `*scratch*', `*Messages*' display as is
(t (propertize
(format "%s" (buffer-name))
'face 'bold))))
;; Project name
(defun gleemacs/mode-line-project-name ()
"Return the current project name for the mode line, or empty string."
(if-let ((proj (ignore-errors (project-current nil))))
(concat " [" (file-name-nondirectory
(directory-file-name (project-root proj))) "] ")
""))
;; VC branch
(defun gleemacs/mode-line-vc-branch ()
"Return the current VC branch name with fancy prefix, or empty string."
(if vc-mode
(let* ((branch (substring-no-properties vc-mode)))
(setq branch (string-trim branch))
;; Format "Git-master" as " master"
(setq branch (replace-regexp-in-string "^[A-Za-z]+[-: ]" "" branch))
(concat " " branch " "))
""))
(defun gleemacs/terminal-mode-p ()
"Return non-nil if the current buffer is a terminal.
Covers:
- vterm
- term
- eshell"
(or
;; vterm: real pty
(eq major-mode 'vterm-mode)
;; term-mode (ansi-term / term): real pty
(eq major-mode 'term-mode)
;; eshell: non-pty but shell-like
(eq major-mode 'eshell-mode)))
;; Major mode
;; Courtesy of Protesilaos Stavrou
(defun gleemacs-modeline-major-mode-indicator ()
"Return appropriate propertised mode line indicator for the major mode."
(let ((indicator (cond
((gleemacs/terminal-mode-p) ">_")
((derived-mode-p 'prog-mode) "λ")
((derived-mode-p 'text-mode) "§")
((derived-mode-p 'comint-mode) ">_")
;; ((derived-mode-p 'vterm-mode) ">_")
(t "◦"))))
(propertize indicator 'face 'shadow)))
;; `mode-line-format' ---------------
;; This configures how the modeline displays. It's basically the default
;; with some subtle improvements for `vc-mode' and buffer naming, mainly
;; focusing on adding project context for the current buffer.
(setq-default mode-line-format
'("%e" mode-line-front-space
(:propertize
("" mode-line-mule-info mode-line-client mode-line-modified
mode-line-remote mode-line-window-dedicated)
display (min-width (6.0)))
mode-line-frame-identification
(:eval (list
;; (or (my/mode-line-project-name)) ""
(gleemacs-modeline-major-mode-indicator) " "
mode-line-buffer-identification ""
;; (gleemacs/mode-line-path) ""
))
" "
mode-line-position
(:eval
(list
(or (gleemacs/mode-line-project-name))
(or (gleemacs/mode-line-vc-branch))
(format-mode-line mode-line-modes)
(string-trim(format-mode-line mode-line-misc-info))))))
uniquify
;; ; `uniquify' (unique names for buffers)
(use-package uniquify
:ensure nil
:custom
(uniquify-separator " | ")
:config
(setq uniquify-buffer-name-style 'forward)
(setq uniquify-strip-common-suffix t)
(setq uniquify-after-kill-buffer-p t))
ace-window
;; ; Window Setup -----------------
;; ; Ace Window
;; Makes multi window movements efficent and predictable
(use-package ace-window
:init
(global-set-key [remap other-window] #'ace-window)
:config
(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
aw-scope 'frame
aw-background t)
winner
;; ; Window history (winner-mode)
(use-package winner
:ensure nil
:hook (after-init . winner-mode)
:bind
(("C-x <right>" . winner-redo)
("C-x <left>" . winner-undo)))
tab-bar
(use-package tab-bar
:ensure nil
:config
(setq tab-bar-new-button-show nil)
(setq tab-bar-close-button-show nil)
(setq tab-bar-show 1))
Footer
;; ; Now providing glee...
(provide 'gleemacs-window)
;;; gleemacs-window.el ends here
Cheers and Inspiration
-
Protesilaos’ (aka Prot) .dotmacs: Source: https://protesilaos.com/emacs/dotemacs
Prot’s config is the gold standard for learning and configuring GNU Emacs. It’s both a tutorial and an expertly written configuration, and is used heavily in gleemacs. If you need someone who knows a package inside and out from a useability standpoint, it’s probably him!
-
James Cherti’s minimal.emacs.d: Source: https://github.com/jamescherti/minimal-emacs.d
James’ config served as the base line for performance standards in gleemacs. His setup is buttery smooth and extremely quick to load, and who doesn’t like that?!
-
Steve Purcell’s emacs.d: Source: https://github.com/purcell/emacs.d
Purcell’s config directly inspired the modular layout of gleemacs. Initially when I read through Prot’s config, it seemed extremely complex, Purcell’s on the other hand (despite being effectively the same as Prot’s…) just clicked; after that, so did Prot’s. His
ibuffersetup was lovingly borrowed as well.