db1c2f46b33fe1d0f588ca0628cec42a451b8db9
[~bandali/configs] / init.org
1 #+title: =aminb='s Literate Emacs Configuration
2 #+author: Amin Bandali
3 #+babel: :cache yes
4 #+property: header-args :tangle yes
5
6 * About
7 :PROPERTIES:
8 :CUSTOM_ID: about
9 :END:
10
11 This org file is my literate configuration for GNU Emacs, and is
12 tangled to [[./init.el][init.el]]. Packages are installed and managed using
13 [[https://github.com/emacscollective/borg][Borg]]. Over the years, I've taken inspiration from configurations of
14 many different people. Some of the configurations that I can remember
15 off the top of my head are:
16
17 - [[https://github.com/dieggsy/dotfiles][dieggsy/dotfiles]]: literate Emacs and dotfiles configuration, uses
18 straight.el for managing packages
19 - [[https://github.com/dakra/dmacs][dakra/dmacs]]: literate Emacs configuration, using Borg for managing
20 packages
21 - [[http://pages.sachachua.com/.emacs.d/Sacha.html][Sacha Chua's literate Emacs configuration]]
22 - [[https://github.com/dakrone/eos][dakrone/eos]]
23 - Ryan Rix's [[http://doc.rix.si/cce/cce.html][Complete Computing Environment]] ([[http://doc.rix.si/projects/fsem.html][about cce]])
24 - [[https://github.com/jwiegley/dot-emacs][jwiegley/dot-emacs]]: nix-based configuration
25 - [[https://github.com/wasamasa/dotemacs][wasamasa/dotemacs]]
26 - [[https://github.com/hlissner/doom-emacs][Doom Emacs]]
27
28 I'd like to have a fully reproducible Emacs setup (part of the reason
29 why I store my configuration in this repository) but unfortunately out
30 of the box, that's not achievable with =package.el=, not currently
31 anyway. So, I've opted to use Borg. For what it's worth, I briefly
32 experimented with [[https://github.com/raxod502/straight.el][straight.el]], but found that it added about 2 seconds
33 to my init time; which is unacceptable for me: I use Emacs as my
34 window manager (via EXWM) and coming from bspwm, I'm too used to
35 having fast startup times.
36
37 ** Installation
38
39 To use this config for your Emacs, first you need to clone this repo,
40 then bootstrap Borg, tell Borg to retrieve package submodules, and
41 byte-compiled the packages. Something along these lines should work:
42
43 #+begin_src sh :tangle no
44 git clone https://github.com/aminb/dotfiles ~/.emacs.d
45 cd ~/.emacs.d
46 make bootstrap-borg
47 make tangle-init
48 make bootstrap
49 make build
50 #+end_src
51
52 * Contents :toc_1:noexport:
53
54 - [[#about][About]]
55 - [[#header][Header]]
56 - [[#initial-setup][Initial setup]]
57 - [[#core][Core]]
58 - [[#post-initialization][Post initialization]]
59 - [[#footer][Footer]]
60
61 * Header
62 :PROPERTIES:
63 :CUSTOM_ID: header
64 :END:
65
66 ** First line
67
68 #+begin_src emacs-lisp :comments none
69 ;;; init.el --- Amin Bandali's Emacs config -*- lexical-binding: t ; eval: (view-mode 1)-*-
70 #+end_src
71
72 Enable =view-mode=, which both makes the file read-only (as a reminder
73 that =init.el= is an auto-generated file, not supposed to be edited),
74 and provides some convenient key bindings for browsing through the
75 file.
76
77 ** License
78
79 #+begin_src emacs-lisp :comments none
80 ;; Copyright (C) 2018 Amin Bandali <amin@aminb.org>
81
82 ;; This program is free software: you can redistribute it and/or modify
83 ;; it under the terms of the GNU General Public License as published by
84 ;; the Free Software Foundation, either version 3 of the License, or
85 ;; (at your option) any later version.
86
87 ;; This program is distributed in the hope that it will be useful,
88 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
89 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
90 ;; GNU General Public License for more details.
91
92 ;; You should have received a copy of the GNU General Public License
93 ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
94 #+end_src
95
96 ** Commentary
97
98 #+begin_src emacs-lisp :comments none
99 ;;; Commentary:
100
101 ;; Emacs configuration of Amin Bandali, computer scientist and functional
102 ;; programmer.
103
104 ;; THIS FILE IS AUTO-GENERATED FROM `init.org'.
105 #+end_src
106
107 ** Naming conventions
108
109 The conventions below were inspired by [[https://github.com/hlissner/doom-emacs][Doom]]'s conventions, found
110 [[https://github.com/hlissner/doom-emacs/blob/5dacbb7cb1c6ac246a9ccd15e6c4290def67757c/core/core.el#L3-L17][here]]. Naturally, I use my initials, =ab=, instead of =doom=.
111
112 #+begin_src emacs-lisp :comments none
113 ;; Naming conventions:
114 ;;
115 ;; ab-... public variables or non-interactive functions
116 ;; ab--... private anything (non-interactive), not safe for direct use
117 ;; ab/... an interactive function; safe for M-x or keybinding
118 ;; ab:... an evil operator, motion, or command
119 ;; ab|... a hook function
120 ;; ab*... an advising function
121 ;; ab@... a hydra command
122 ;; ...! a macro
123 #+end_src
124
125 * Initial setup
126 :PROPERTIES:
127 :CUSTOM_ID: initial-setup
128 :END:
129
130 #+begin_src emacs-lisp :comments none
131 ;;; Code:
132 #+end_src
133
134 ** Emacs initialization
135
136 I'd like to do a couple of measurements of Emacs' startup time. First,
137 let's see how long Emacs takes to start up, before even loading
138 =init.el=, i.e. =user-init-file=:
139
140 #+begin_src emacs-lisp
141 (defvar ab--before-user-init-time (current-time)
142 "Value of `current-time' when Emacs begins loading `user-init-file'.")
143 (message "Loading Emacs...done (%.3fs)"
144 (float-time (time-subtract ab--before-user-init-time
145 before-init-time)))
146 #+end_src
147
148 Also, temporarily increase ~gc-cons-threshhold~ and
149 ~gc-cons-percentage~ during startup to reduce garbage collection
150 frequency. Clearing the ~file-name-handler-alist~ seems to help reduce
151 startup time as well.
152
153 #+begin_src emacs-lisp
154 (defvar ab--gc-cons-threshold gc-cons-threshold)
155 (defvar ab--gc-cons-percentage gc-cons-percentage)
156 (defvar ab--file-name-handler-alist file-name-handler-alist)
157 (setq gc-cons-threshold (* 400 1024 1024) ; 400 MiB
158 gc-cons-percentage 0.6
159 file-name-handler-alist nil
160 ;; sidesteps a bug when profiling with esup
161 esup-child-profile-require-level 0)
162 #+end_src
163
164 Of course, we'd like to set them back to their defaults once we're
165 done initializing.
166
167 #+begin_src emacs-lisp
168 (add-hook
169 'after-init-hook
170 (lambda ()
171 (setq gc-cons-threshold ab--gc-cons-threshold
172 gc-cons-percentage ab--gc-cons-percentage
173 file-name-handler-alist ab--file-name-handler-alist)))
174 #+end_src
175
176 Increase the number of lines kept in message logs (the =*Messages*=
177 buffer).
178
179 #+begin_src emacs-lisp
180 (setq message-log-max 20000)
181 #+end_src
182
183 Optionally, we could suppress some byte compiler warnings like below,
184 but for now I've decided to keep them enabled. See documentation for
185 ~byte-compile-warnings~ for more details.
186
187 #+begin_src emacs-lisp
188 ;; (setq byte-compile-warnings
189 ;; '(not free-vars unresolved noruntime lexical make-local))
190 #+end_src
191
192 ** Package management
193
194 *** No =package.el=
195
196 I can do all my package management things with Borg, and don't need
197 Emacs' built-in =package.el=. Emacs 27 lets us disable =package.el= in
198 the =early-init-file= (see [[https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=24acb31c04b4048b85311d794e600ecd7ce60d3b][here]]).
199
200 #+begin_src emacs-lisp :tangle early-init.el
201 (setq package-enable-at-startup nil)
202 #+end_src
203
204 But since Emacs 27 isn't out yet (Emacs 26 is just around the corner
205 right now), and even when released it'll be long before most distros
206 ship in their repos, I'll still put the old workaround with the
207 commented call to ~package-initialize~ here anyway.
208
209 #+begin_src emacs-lisp
210 (setq package-enable-at-startup nil)
211 ;; (package-initialize)
212 #+end_src
213
214 *** Borg
215
216 #+begin_quote
217 Assimilate Emacs packages as Git submodules
218 #+end_quote
219
220 [[https://github.com/emacscollective/borg][Borg]] is at the heart of package management of my Emacs setup. In
221 short, it creates a git submodule in =lib/= for each package, which
222 can then be managed with the help of Magit or other tools.
223
224 #+begin_src emacs-lisp
225 (setq user-init-file (or load-file-name buffer-file-name)
226 user-emacs-directory (file-name-directory user-init-file))
227 (add-to-list 'load-path
228 (expand-file-name "lib/borg" user-emacs-directory))
229 (require 'borg)
230 (borg-initialize)
231 #+end_src
232
233 *** =use-package=
234
235 #+begin_quote
236 A use-package declaration for simplifying your .emacs
237 #+end_quote
238
239 [[https://github.com/jwiegley/use-package][use-package]] is an awesome utility for managing and configuring
240 packages (in our case especially the latter) in a neatly organized way
241 and without compromising on performance.
242
243 #+begin_src emacs-lisp
244 (require 'use-package)
245 (if nil ; set to t when need to debug init
246 (setq use-package-verbose t
247 use-package-expand-minimally nil
248 use-package-compute-statistics t
249 debug-on-error t)
250 (setq use-package-verbose nil
251 use-package-expand-minimally t))
252 #+end_src
253
254 *** Epkg
255
256 #+begin_quote
257 Browse the Emacsmirror package database
258 #+end_quote
259
260 Epkg provides access to a local copy of the [[https://emacsmirror.net][Emacsmirror]] package
261 database, low-level functions for querying the database, and a
262 =package.el=-like user interface for browsing the available packages.
263
264 #+begin_src emacs-lisp
265 (use-package epkg
266 :defer t)
267 #+end_src
268
269 ** No littering in =~/.emacs.d=
270
271 #+begin_quote
272 Help keeping ~/.emacs.d clean
273 #+end_quote
274
275 By default, even for Emacs' built-in packages, the configuration files
276 and persistent data are all over the place. Use =no-littering= to help
277 contain the mess.
278
279 #+begin_src emacs-lisp
280 (use-package no-littering
281 :demand t
282 :config
283 (savehist-mode 1)
284 (add-to-list 'savehist-additional-variables 'kill-ring)
285 (save-place-mode 1)
286 (setq auto-save-file-name-transforms
287 `((".*" ,(no-littering-expand-var-file-name "auto-save/") t))))
288 #+end_src
289
290 ** Custom file (=custom.el=)
291
292 I'm not planning on using the custom file much, but even so, I
293 definitely don't want it mixing with =init.el=. So, here; let's give
294 it it's own file. While at it, treat themes as safe.
295
296 #+begin_src emacs-lisp
297 (use-package custom
298 :no-require t
299 :config
300 (setq custom-file (no-littering-expand-etc-file-name "custom.el"))
301 (when (file-exists-p custom-file)
302 (load custom-file))
303 (setf custom-safe-themes t))
304 #+end_src
305
306 ** Better =$PATH= handling
307
308 Let's use [[https://github.com/purcell/exec-path-from-shell][exec-path-from-shell]] to make Emacs use the =$PATH= as set up
309 in my shell.
310
311 #+begin_src emacs-lisp
312 (use-package exec-path-from-shell
313 :defer 1
314 :init
315 (setq exec-path-from-shell-check-startup-files nil)
316 :config
317 (exec-path-from-shell-initialize)
318 ;; while we're at it, let's fix access to our running ssh-agent
319 (exec-path-from-shell-copy-env "SSH_AGENT_PID")
320 (exec-path-from-shell-copy-env "SSH_AUTH_SOCK"))
321 #+end_src
322
323 ** Server
324
325 Start server if not already running. Alternatively, can be done by
326 issuing =emacs --daemon= in the terminal, which can be automated with
327 a systemd service or using =brew services start emacs= on macOS. I use
328 Emacs as my window manager (via EXWM), so I always start Emacs on
329 login; so starting the server from inside Emacs is good enough for me.
330
331 See [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Server.html#Emacs-Server][Using Emacs as a Server]].
332
333 #+begin_src emacs-lisp
334 (use-package server
335 :config (or (server-running-p) (server-mode)))
336 #+end_src
337
338 ** Unicode support
339
340 Font stack with better unicode support, around =Ubuntu Mono= and
341 =Hack=.
342
343 #+begin_src emacs-lisp
344 (dolist (ft (fontset-list))
345 (set-fontset-font
346 ft
347 'unicode
348 (font-spec :name "Ubuntu Mono"))
349 (set-fontset-font
350 ft
351 'unicode
352 (font-spec :name "DejaVu Sans Mono")
353 nil
354 'append)
355 ;; (set-fontset-font
356 ;; ft
357 ;; 'unicode
358 ;; (font-spec
359 ;; :name "Symbola monospacified for DejaVu Sans Mono")
360 ;; nil
361 ;; 'append)
362 ;; (set-fontset-font
363 ;; ft
364 ;; #x2115 ; â„•
365 ;; (font-spec :name "DejaVu Sans Mono")
366 ;; nil
367 ;; 'append)
368 (set-fontset-font
369 ft
370 (cons ?Α ?ω)
371 (font-spec :name "DejaVu Sans Mono" :size 14)
372 nil
373 'prepend))
374 #+end_src
375
376 * Core
377 :PROPERTIES:
378 :CUSTOM_ID: core
379 :END:
380
381 ** Defaults
382
383 *** Time and battery in mode-line
384
385 Enable displaying time and battery in the mode-line, since I'm not
386 using the Xfce panel anymore. Also, I don't need to see the load
387 average on a regular basis, so disable that.
388
389 #+begin_src emacs-lisp
390 (use-package time
391 :ensure nil
392 :init
393 (setq display-time-default-load-average nil)
394 :config
395 (display-time-mode))
396
397 (use-package battery
398 :ensure nil
399 :config
400 (display-battery-mode))
401 #+end_src
402
403 *** Smaller fringe
404
405 Might want to set the fringe to a smaller value, especially if using
406 EXWM. I'm fine with the default for now.
407
408 #+begin_src emacs-lisp
409 ;; (fringe-mode '(3 . 1))
410 (fringe-mode nil)
411 #+end_src
412
413 *** Disable disabled commands
414
415 Emacs disables some commands by default that could persumably be
416 confusing for novice users. Let's disable that.
417
418 #+begin_src emacs-lisp
419 (setq disabled-command-function nil)
420 #+end_src
421
422 *** Kill-ring
423
424 Save what I copy into clipboard from other applications into Emacs'
425 kill-ring, which would allow me to still be able to easily access it
426 in case I kill (cut or copy) something else inside Emacs before
427 yanking (pasting) what I'd originally intended to.
428
429 #+begin_src emacs-lisp
430 (setq save-interprogram-paste-before-kill t)
431 #+end_src
432
433 *** Minibuffer
434
435 #+begin_src emacs-lisp
436 (setq enable-recursive-minibuffers t
437 resize-mini-windows t)
438 #+end_src
439
440 *** Lazy-person-friendly yes/no prompts
441
442 Lazy people would prefer to type fewer keystrokes, especially for yes
443 or no questions. I'm lazy.
444
445 #+begin_src emacs-lisp
446 (defalias 'yes-or-no-p #'y-or-n-p)
447 #+end_src
448
449 *** Startup screen and =*scratch*=
450
451 Firstly, let Emacs know that I'd like to have =*scratch*= as my
452 startup buffer.
453
454 #+begin_src emacs-lisp
455 (setq initial-buffer-choice t)
456 #+end_src
457
458 Now let's customize the =*scratch*= buffer a bit. First off, I don't
459 need the default hint.
460
461 #+begin_src emacs-lisp
462 (setq initial-scratch-message nil)
463 #+end_src
464
465 Also, let's use Text mode as the major mode, in case I want to
466 customize it (=*scratch*='s default major mode, Fundamental mode,
467 can't really be customized).
468
469 #+begin_src emacs-lisp
470 (setq initial-major-mode 'text-mode)
471 #+end_src
472
473 Inhibit the buffer list when more than 2 files are loaded.
474
475 #+begin_src emacs-lisp
476 (setq inhibit-startup-buffer-menu t)
477 #+end_src
478
479 I don't really need to see the startup screen or echo area message
480 either.
481
482 #+begin_src emacs-lisp
483 (advice-add #'display-startup-echo-area-message :override #'ignore)
484 (setq inhibit-startup-screen t
485 inhibit-startup-echo-area-message user-login-name)
486 #+end_src
487
488 *** More useful frame titles
489
490 Show either the file name or the buffer name (in case the buffer isn't
491 visiting a file). Borrowed from Emacs Prelude.
492
493 #+begin_src emacs-lisp
494 (setq frame-title-format
495 '("" invocation-name " - "
496 (:eval (if (buffer-file-name)
497 (abbreviate-file-name (buffer-file-name))
498 "%b"))))
499 #+end_src
500
501 *** Backups
502
503 Emacs' default backup settings aren't that great. Let's use more
504 sensible options. See documentation for the ~make-backup-file~
505 variable.
506
507 #+begin_src emacs-lisp
508 (setq backup-by-copying t
509 version-control t)
510 #+end_src
511
512 ** Packages
513
514 The packages in this section are absolutely essential to my everyday
515 workflow, and they play key roles in how I do my computing. They
516 immensely enhance the Emacs experience for me; both using Emacs, and
517 customizing it.
518
519 *** [[https://github.com/emacscollective/auto-compile][auto-compile]]
520
521 #+begin_src emacs-lisp
522 (use-package auto-compile
523 :demand t
524 :config
525 (auto-compile-on-load-mode)
526 (auto-compile-on-save-mode)
527 (setq auto-compile-display-buffer nil
528 auto-compile-mode-line-counter t
529 auto-compile-source-recreate-deletes-dest t
530 auto-compile-toggle-deletes-nonlib-dest t
531 auto-compile-update-autoloads t)
532 (add-hook 'auto-compile-inhibit-compile-hook
533 'auto-compile-inhibit-compile-detached-git-head))
534 #+end_src
535
536 *** TODO [[https://github.com/Kungsgeten/ryo-modal][ryo-modal]]
537
538 #+begin_quote
539 Roll your own modal mode
540 #+end_quote
541
542 *** [[https://github.com/ch11ng/exwm][EXWM]] (window manager)
543
544 #+begin_src emacs-lisp :tangle no
545 (use-package exwm
546 :demand t
547 :config
548 (require 'exwm-config)
549
550 ;; Set the initial workspace number.
551 (setq exwm-workspace-number 4)
552
553 ;; Make class name the buffer name, truncating beyond 50 characters
554 (defun exwm-rename-buffer ()
555 (interactive)
556 (exwm-workspace-rename-buffer
557 (concat exwm-class-name ":"
558 (if (<= (length exwm-title) 50) exwm-title
559 (concat (substring exwm-title 0 49) "...")))))
560 (add-hook 'exwm-update-class-hook 'exwm-rename-buffer)
561 (add-hook 'exwm-update-title-hook 'exwm-rename-buffer)
562
563 ;; 's-R': Reset
564 (exwm-input-set-key (kbd "s-R") #'exwm-reset)
565 ;; 's-\': Switch workspace
566 (exwm-input-set-key (kbd "s-\\") #'exwm-workspace-switch)
567 ;; 's-N': Switch to certain workspace
568 (dotimes (i 10)
569 (exwm-input-set-key (kbd (format "s-%d" i))
570 (lambda ()
571 (interactive)
572 (exwm-workspace-switch-create i))))
573 ;; 's-SPC': Launch application
574 ;; (exwm-input-set-key
575 ;; (kbd "s-SPC")
576 ;; (lambda (command)
577 ;; (interactive (list (read-shell-command "➜ ")))
578 ;; (start-process-shell-command command nil command)))
579
580 (exwm-input-set-key (kbd "M-s-SPC") #'counsel-linux-app)
581
582 ;; Shorten 'C-c C-q' to 'C-q'
583 (define-key exwm-mode-map [?\C-q] #'exwm-input-send-next-key)
584
585 ;; Line-editing shortcuts
586 (setq exwm-input-simulation-keys
587 '(;; movement
588 ([?\C-b] . [left])
589 ([?\M-b] . [C-left])
590 ([?\C-f] . [right])
591 ([?\M-f] . [C-right])
592 ([?\C-p] . [up])
593 ([?\C-n] . [down])
594 ([?\C-a] . [home])
595 ([?\C-e] . [end])
596 ([?\M-v] . [prior])
597 ([?\C-v] . [next])
598 ([?\C-d] . [delete])
599 ([?\C-k] . [S-end delete])
600 ;; cut/copy/paste
601 ;; ([?\C-w] . [?\C-x])
602 ([?\M-w] . [?\C-c])
603 ([?\C-y] . [?\C-v])
604 ;; search
605 ([?\C-s] . [?\C-f])))
606
607 ;; Enable EXWM
608 (exwm-enable)
609
610 (add-hook 'exwm-init-hook #'exwm-config--fix/ido-buffer-window-other-frame)
611
612 (require 'exwm-systemtray)
613 (exwm-systemtray-enable)
614
615 (require 'exwm-randr)
616 (exwm-randr-enable)
617
618 ;; (exwm-input-set-key
619 ;; (kbd "s-<return>")
620 ;; (lambda ()
621 ;; (interactive)
622 ;; (start-process "urxvt" nil "urxvt")))
623
624 ;; (exwm-input-set-key
625 ;; (kbd "s-SPC") ;; rofi doesn't properly launch programs when started from emacs
626 ;; (lambda ()
627 ;; (interactive)
628 ;; (start-process-shell-command "rofi-run" nil "rofi -show run -display-run '> ' -display-window ' 🗔 '")))
629
630 ;; (exwm-input-set-key
631 ;; (kbd "s-/")
632 ;; (lambda ()
633 ;; (interactive)
634 ;; (start-process-shell-command "rofi-win" nil "rofi -show window -display-run '> ' -display-window ' 🗔 '")))
635
636 ;; (exwm-input-set-key
637 ;; (kbd "M-SPC")
638 ;; (lambda ()
639 ;; (interactive)
640 ;; (start-process "rofi-pass" nil "rofi-pass")))
641
642 ;; (exwm-input-set-key
643 ;; (kbd "<XF86AudioMute>")
644 ;; (lambda ()
645 ;; (interactive)
646 ;; (start-process-shell-command "pamixer" nil "pamixer --toggle-mute")))
647
648 ;; (exwm-input-set-key
649 ;; (kbd "<XF86AudioLowerVolume>")
650 ;; (lambda ()
651 ;; (interactive)
652 ;; (start-process-shell-command "pamixer" nil "pamixer --allow-boost --decrease 5")))
653
654 ;; (exwm-input-set-key
655 ;; (kbd "<XF86AudioRaiseVolume>")
656 ;; (lambda ()
657 ;; (interactive)
658 ;; (start-process-shell-command "pamixer" nil "pamixer --allow-boost --increase 5")))
659
660 ;; (exwm-input-set-key
661 ;; (kbd "<XF86AudioPlay>")
662 ;; (lambda ()
663 ;; (interactive)
664 ;; (start-process-shell-command "mpc" nil "mpc toggle")))
665
666 ;; (exwm-input-set-key
667 ;; (kbd "<XF86AudioPrev>")
668 ;; (lambda ()
669 ;; (interactive)
670 ;; (start-process-shell-command "mpc" nil "mpc prev")))
671
672 ;; (exwm-input-set-key
673 ;; (kbd "<XF86AudioNext>")
674 ;; (lambda ()
675 ;; (interactive)
676 ;; (start-process-shell-command "mpc" nil "mpv next")))
677
678 (defun ab--exwm-pasystray ()
679 "A command used to start pasystray."
680 (interactive)
681 (if (executable-find "pasystray")
682 (progn
683 (message "EXWM: starting pasystray ...")
684 (start-process-shell-command "pasystray" nil "pasystray --notify=all"))
685 (message "EXWM: pasystray is not installed, abort!")))
686
687 (add-hook 'exwm-init-hook #'ab--exwm-pasystray)
688
689 (exwm-input-set-key
690 (kbd "s-t")
691 (lambda ()
692 (interactive)
693 (exwm-floating-toggle-floating)))
694
695 (exwm-input-set-key
696 (kbd "s-f")
697 (lambda ()
698 (interactive)
699 (exwm-layout-toggle-fullscreen)))
700
701 (exwm-input-set-key
702 (kbd "s-w")
703 (lambda ()
704 (interactive)
705 (kill-buffer (current-buffer))))
706
707 (exwm-input-set-key
708 (kbd "s-q")
709 (lambda ()
710 (interactive)
711 (exwm-manage--kill-client))))
712 #+end_src
713
714 **** sxhkdrc
715 :PROPERTIES:
716 :header-args+: :tangle ~/.config/sxhkd/sxhkdrc :mkdirp yes
717 :END:
718
719 #+begin_src conf :tangle no
720 # terminal emulator
721 super + Return
722 urxvt
723
724 # program launcher
725 super + space
726 rofi -show run -display-run '> ' -display-window ' 🗔 '
727
728 # window finder
729 super + slash
730 rofi -show window -display-run '> ' -display-window ' 🗔 '
731
732 # password manager
733 alt + space
734 rofi-pass
735
736 # make sxhkd reload its configuration files:
737 super + Escape
738 pkill -USR1 -x sxhkd
739
740 # volume {up,down}
741 XF86Audio{Raise,Lower}Volume
742 pamixer --allow-boost --{in,de}crease 5
743
744 # mute
745 XF86AudioMute
746 pamixer --toggle-mute
747
748 # playback control
749 XF86Audio{Play,Prev,Next}
750 mpc {toggle,prev,next}
751
752 # Toggle keyboard layout
753 # super + F7
754 # toggle-layout
755
756 # Toggle Xfce presentation mode
757 # XF86LaunchB
758 # toggle-presentation-mode
759
760 # monitor brightness
761 XF86MonBrightness{Up,Down}
762 light -{A,U} 5
763
764 super + apostrophe
765 rofi-light
766 #+end_src
767
768 *** [[https://orgmode.org/][Org mode]]
769
770 #+begin_quote
771 Org mode is for keeping notes, maintaining TODO lists, planning
772 projects, and authoring documents with a fast and effective plain-text
773 system.
774 #+end_quote
775
776 In short, my favourite way of life.
777
778 #+begin_src emacs-lisp
779 (setq org-src-tab-acts-natively t
780 org-src-preserve-indentation nil
781 org-edit-src-content-indentation 0)
782 #+end_src
783
784 *** [[https://magit.vc/][Magit]]
785
786 #+begin_quote
787 It's Magit! A Git porcelain inside Emacs.
788 #+end_quote
789
790 Not just how I do git, but /the/ way to do git.
791
792 #+begin_src emacs-lisp
793 (use-package magit
794 :defer t
795 :bind (("s-g" . magit-status)
796 ("C-x g" . magit-status)
797 ("C-x M-g" . magit-dispatch-popup))
798 :config
799 (magit-add-section-hook 'magit-status-sections-hook
800 'magit-insert-modules
801 'magit-insert-stashes
802 'append))
803 #+end_src
804
805 *** [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
806
807 #+begin_quote
808 Ivy - a generic completion frontend for Emacs, Swiper - isearch with
809 an overview, and more. Oh, man!
810 #+end_quote
811
812 There's no way I could top that, so I won't attempt to.
813
814 **** Ivy
815
816 #+begin_src emacs-lisp
817 (use-package ivy
818 :bind
819 (:map ivy-minibuffer-map
820 ([escape] . keyboard-escape-quit)
821 ;; ("C-j" . ivy-next-line)
822 ;; ("C-k" . ivy-previous-line)
823 ([S-up] . ivy-previous-history-element)
824 ([S-down] . ivy-next-history-element)
825 ("DEL" . ivy-backward-delete-char))
826 :config
827 (setq ivy-wrap t)
828 (ivy-mode 1))
829 #+end_src
830
831 **** Swiper
832
833 #+begin_src emacs-lisp
834 (use-package swiper
835 :bind (([remap isearch-forward] . swiper)
836 ([remap isearch-backward] . swiper)))
837 #+end_src
838
839 **** Counsel
840
841 #+begin_src emacs-lisp
842 (use-package counsel
843 :defer 1
844 :bind (([remap execute-extended-command] . counsel-M-x)
845 ([remap find-file] . counsel-find-file)
846 ("s-r" . counsel-recentf)
847 :map minibuffer-local-map
848 ("C-r" . counsel-minibuffer-history))
849 :config
850 (counsel-mode 1)
851 (defalias 'locate #'counsel-locate))
852 #+end_src
853
854 * Borg's =layer/essentials=
855
856 TODO: break this giant source block down into individual org sections.
857
858 #+begin_src emacs-lisp
859 (use-package dash
860 :config (dash-enable-font-lock))
861
862 (use-package diff-hl
863 :config
864 (setq diff-hl-draw-borders nil)
865 (global-diff-hl-mode)
866 (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh t))
867
868 (use-package dired
869 :defer t
870 :config (setq dired-listing-switches "-alh"))
871
872 (use-package eldoc
873 :when (version< "25" emacs-version)
874 :config (global-eldoc-mode))
875
876 (use-package help
877 :defer t
878 :config (temp-buffer-resize-mode))
879
880 (progn ; `isearch'
881 (setq isearch-allow-scroll t))
882
883 (use-package lisp-mode
884 :config
885 (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
886 (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
887 (defun indent-spaces-mode ()
888 (setq indent-tabs-mode nil))
889 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
890
891 (use-package man
892 :defer t
893 :config (setq Man-width 80))
894
895 (use-package paren
896 :config (show-paren-mode))
897
898 (use-package prog-mode
899 :config (global-prettify-symbols-mode)
900 (defun indicate-buffer-boundaries-left ()
901 (setq indicate-buffer-boundaries 'left))
902 (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
903
904 (use-package recentf
905 :demand t
906 :config (add-to-list 'recentf-exclude "^/\\(?:ssh\\|su\\|sudo\\)?:"))
907
908 (use-package savehist
909 :config (savehist-mode))
910
911 (use-package saveplace
912 :when (version< "25" emacs-version)
913 :config (save-place-mode))
914
915 (use-package simple
916 :config (column-number-mode))
917
918 (progn ; `text-mode'
919 (add-hook 'text-mode-hook #'indicate-buffer-boundaries-left))
920
921 (use-package tramp
922 :defer t
923 :config
924 (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
925 (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
926 (add-to-list 'tramp-default-proxies-alist
927 (list (regexp-quote (system-name)) nil nil)))
928
929 (use-package undo-tree
930 :config
931 (global-undo-tree-mode)
932 (setq undo-tree-mode-lighter ""))
933 #+end_src
934
935 * Programming modes
936
937 ** Lean mode
938
939 #+begin_src emacs-lisp
940 (use-package lean-mode
941 :bind (:map lean-mode-map
942 ("S-SPC" . company-complete)))
943 #+end_src
944
945 * Post initialization
946 :PROPERTIES:
947 :CUSTOM_ID: post-initialization
948 :END:
949
950 Display how long it took to load the init file.
951
952 #+begin_src emacs-lisp
953 (message "Loading %s...done (%.3fs)" user-init-file
954 (float-time (time-subtract (current-time)
955 ab--before-user-init-time)))
956 #+end_src
957
958 * Footer
959 :PROPERTIES:
960 :CUSTOM_ID: footer
961 :END:
962
963 #+begin_src emacs-lisp :comments none
964 ;;; init.el ends here
965 #+end_src