a76ce5ec3d0209a0e6964143caeceafe3f17a033
[~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
353 :name "Hack")
354 nil
355 'append)
356 (set-fontset-font
357 ft
358 'unicode
359 (font-spec
360 :name "Symbola monospacified for DejaVu Sans Mono")
361 nil
362 'append))
363 #+end_src
364
365 * Core
366 :PROPERTIES:
367 :CUSTOM_ID: core
368 :END:
369
370 ** Defaults
371
372 *** Time and battery in mode-line
373
374 Enable displaying time and battery in the mode-line, since I'm not
375 using the Xfce panel anymore. Also, I don't need to see the load
376 average on a regular basis, so disable that.
377
378 #+begin_src emacs-lisp
379 (use-package time
380 :ensure nil
381 :init
382 (setq display-time-default-load-average nil)
383 :config
384 (display-time-mode))
385
386 (use-package battery
387 :ensure nil
388 :config
389 (display-battery-mode))
390 #+end_src
391
392 *** Smaller fringe
393
394 Might want to set the fringe to a smaller value, especially if using
395 EXWM. I'm fine with the default for now.
396
397 #+begin_src emacs-lisp
398 ;; (fringe-mode '(3 . 1))
399 (fringe-mode nil)
400 #+end_src
401
402 *** Disable disabled commands
403
404 Emacs disables some commands by default that could persumably be
405 confusing for novice users. Let's disable that.
406
407 #+begin_src emacs-lisp
408 (setq disabled-command-function nil)
409 #+end_src
410
411 *** Kill-ring
412
413 Save what I copy into clipboard from other applications into Emacs'
414 kill-ring, which would allow me to still be able to easily access it
415 in case I kill (cut or copy) something else inside Emacs before
416 yanking (pasting) what I'd originally intended to.
417
418 #+begin_src emacs-lisp
419 (setq save-interprogram-paste-before-kill t)
420 #+end_src
421
422 *** Minibuffer
423
424 #+begin_src emacs-lisp
425 (setq enable-recursive-minibuffers t
426 resize-mini-windows t)
427 #+end_src
428
429 *** Lazy-person-friendly yes/no prompts
430
431 Lazy people would prefer to type fewer keystrokes, especially for yes
432 or no questions. I'm lazy.
433
434 #+begin_src emacs-lisp
435 (defalias 'yes-or-no-p #'y-or-n-p)
436 #+end_src
437
438 *** Startup screen and =*scratch*=
439
440 Firstly, let Emacs know that I'd like to have =*scratch*= as my
441 startup buffer.
442
443 #+begin_src emacs-lisp
444 (setq initial-buffer-choice t)
445 #+end_src
446
447 Now let's customize the =*scratch*= buffer a bit. First off, I don't
448 need the default hint.
449
450 #+begin_src emacs-lisp
451 (setq initial-scratch-message nil)
452 #+end_src
453
454 Also, let's use Text mode as the major mode, in case I want to
455 customize it (=*scratch*='s default major mode, Fundamental mode,
456 can't really be customized).
457
458 #+begin_src emacs-lisp
459 (setq initial-major-mode 'text-mode)
460 #+end_src
461
462 Inhibit the buffer list when more than 2 files are loaded.
463
464 #+begin_src emacs-lisp
465 (setq inhibit-startup-buffer-menu t)
466 #+end_src
467
468 I don't really need to see the startup screen or echo area message
469 either.
470
471 #+begin_src emacs-lisp
472 (advice-add #'display-startup-echo-area-message :override #'ignore)
473 (setq inhibit-startup-screen t
474 inhibit-startup-echo-area-message user-login-name)
475 #+end_src
476
477 *** More useful frame titles
478
479 Show either the file name or the buffer name (in case the buffer isn't
480 visiting a file). Borrowed from Emacs Prelude.
481
482 #+begin_src emacs-lisp
483 (setq frame-title-format
484 '("" invocation-name " - "
485 (:eval (if (buffer-file-name)
486 (abbreviate-file-name (buffer-file-name))
487 "%b"))))
488 #+end_src
489
490 *** Backups
491
492 Emacs' default backup settings aren't that great. Let's use more
493 sensible options. See documentation for the ~make-backup-file~
494 variable.
495
496 #+begin_src emacs-lisp
497 (setq backup-by-copying t
498 version-control t)
499 #+end_src
500
501 ** Packages
502
503 The packages in this section are absolutely essential to my everyday
504 workflow, and they play key roles in how I do my computing. They
505 immensely enhance the Emacs experience for me; both using Emacs, and
506 customizing it.
507
508 *** [[https://github.com/emacscollective/auto-compile][auto-compile]]
509
510 #+begin_src emacs-lisp
511 (use-package auto-compile
512 :demand t
513 :config
514 (auto-compile-on-load-mode)
515 (auto-compile-on-save-mode)
516 (setq auto-compile-display-buffer nil
517 auto-compile-mode-line-counter t
518 auto-compile-source-recreate-deletes-dest t
519 auto-compile-toggle-deletes-nonlib-dest t
520 auto-compile-update-autoloads t)
521 (add-hook 'auto-compile-inhibit-compile-hook
522 'auto-compile-inhibit-compile-detached-git-head))
523 #+end_src
524
525 *** TODO [[https://github.com/Kungsgeten/ryo-modal][ryo-modal]]
526
527 #+begin_quote
528 Roll your own modal mode
529 #+end_quote
530
531 *** [[https://github.com/ch11ng/exwm][EXWM]] (window manager)
532
533 #+begin_src emacs-lisp :tangle no
534 (use-package exwm
535 :demand t
536 :config
537 (require 'exwm-config)
538
539 ;; Set the initial workspace number.
540 (setq exwm-workspace-number 4)
541
542 ;; Make class name the buffer name, truncating beyond 50 characters
543 (defun exwm-rename-buffer ()
544 (interactive)
545 (exwm-workspace-rename-buffer
546 (concat exwm-class-name ":"
547 (if (<= (length exwm-title) 50) exwm-title
548 (concat (substring exwm-title 0 49) "...")))))
549 (add-hook 'exwm-update-class-hook 'exwm-rename-buffer)
550 (add-hook 'exwm-update-title-hook 'exwm-rename-buffer)
551
552 ;; 's-R': Reset
553 (exwm-input-set-key (kbd "s-R") #'exwm-reset)
554 ;; 's-\': Switch workspace
555 (exwm-input-set-key (kbd "s-\\") #'exwm-workspace-switch)
556 ;; 's-N': Switch to certain workspace
557 (dotimes (i 10)
558 (exwm-input-set-key (kbd (format "s-%d" i))
559 (lambda ()
560 (interactive)
561 (exwm-workspace-switch-create i))))
562 ;; 's-SPC': Launch application
563 ;; (exwm-input-set-key
564 ;; (kbd "s-SPC")
565 ;; (lambda (command)
566 ;; (interactive (list (read-shell-command "➜ ")))
567 ;; (start-process-shell-command command nil command)))
568
569 (exwm-input-set-key (kbd "M-s-SPC") #'counsel-linux-app)
570
571 ;; Shorten 'C-c C-q' to 'C-q'
572 (define-key exwm-mode-map [?\C-q] #'exwm-input-send-next-key)
573
574 ;; Line-editing shortcuts
575 (setq exwm-input-simulation-keys
576 '(;; movement
577 ([?\C-b] . [left])
578 ([?\M-b] . [C-left])
579 ([?\C-f] . [right])
580 ([?\M-f] . [C-right])
581 ([?\C-p] . [up])
582 ([?\C-n] . [down])
583 ([?\C-a] . [home])
584 ([?\C-e] . [end])
585 ([?\M-v] . [prior])
586 ([?\C-v] . [next])
587 ([?\C-d] . [delete])
588 ([?\C-k] . [S-end delete])
589 ;; cut/copy/paste
590 ;; ([?\C-w] . [?\C-x])
591 ([?\M-w] . [?\C-c])
592 ([?\C-y] . [?\C-v])
593 ;; search
594 ([?\C-s] . [?\C-f])))
595
596 ;; Enable EXWM
597 (exwm-enable)
598
599 (add-hook 'exwm-init-hook #'exwm-config--fix/ido-buffer-window-other-frame)
600
601 (require 'exwm-systemtray)
602 (exwm-systemtray-enable)
603
604 (require 'exwm-randr)
605 (exwm-randr-enable)
606
607 ;; (exwm-input-set-key
608 ;; (kbd "s-<return>")
609 ;; (lambda ()
610 ;; (interactive)
611 ;; (start-process "urxvt" nil "urxvt")))
612
613 ;; (exwm-input-set-key
614 ;; (kbd "s-SPC") ;; rofi doesn't properly launch programs when started from emacs
615 ;; (lambda ()
616 ;; (interactive)
617 ;; (start-process-shell-command "rofi-run" nil "rofi -show run -display-run '> ' -display-window ' 🗔 '")))
618
619 ;; (exwm-input-set-key
620 ;; (kbd "s-/")
621 ;; (lambda ()
622 ;; (interactive)
623 ;; (start-process-shell-command "rofi-win" nil "rofi -show window -display-run '> ' -display-window ' 🗔 '")))
624
625 ;; (exwm-input-set-key
626 ;; (kbd "M-SPC")
627 ;; (lambda ()
628 ;; (interactive)
629 ;; (start-process "rofi-pass" nil "rofi-pass")))
630
631 ;; (exwm-input-set-key
632 ;; (kbd "<XF86AudioMute>")
633 ;; (lambda ()
634 ;; (interactive)
635 ;; (start-process-shell-command "pamixer" nil "pamixer --toggle-mute")))
636
637 ;; (exwm-input-set-key
638 ;; (kbd "<XF86AudioLowerVolume>")
639 ;; (lambda ()
640 ;; (interactive)
641 ;; (start-process-shell-command "pamixer" nil "pamixer --allow-boost --decrease 5")))
642
643 ;; (exwm-input-set-key
644 ;; (kbd "<XF86AudioRaiseVolume>")
645 ;; (lambda ()
646 ;; (interactive)
647 ;; (start-process-shell-command "pamixer" nil "pamixer --allow-boost --increase 5")))
648
649 ;; (exwm-input-set-key
650 ;; (kbd "<XF86AudioPlay>")
651 ;; (lambda ()
652 ;; (interactive)
653 ;; (start-process-shell-command "mpc" nil "mpc toggle")))
654
655 ;; (exwm-input-set-key
656 ;; (kbd "<XF86AudioPrev>")
657 ;; (lambda ()
658 ;; (interactive)
659 ;; (start-process-shell-command "mpc" nil "mpc prev")))
660
661 ;; (exwm-input-set-key
662 ;; (kbd "<XF86AudioNext>")
663 ;; (lambda ()
664 ;; (interactive)
665 ;; (start-process-shell-command "mpc" nil "mpv next")))
666
667 (defun ab--exwm-pasystray ()
668 "A command used to start pasystray."
669 (interactive)
670 (if (executable-find "pasystray")
671 (progn
672 (message "EXWM: starting pasystray ...")
673 (start-process-shell-command "pasystray" nil "pasystray --notify=all"))
674 (message "EXWM: pasystray is not installed, abort!")))
675
676 (add-hook 'exwm-init-hook #'ab--exwm-pasystray)
677
678 (exwm-input-set-key
679 (kbd "s-t")
680 (lambda ()
681 (interactive)
682 (exwm-floating-toggle-floating)))
683
684 (exwm-input-set-key
685 (kbd "s-f")
686 (lambda ()
687 (interactive)
688 (exwm-layout-toggle-fullscreen)))
689
690 (exwm-input-set-key
691 (kbd "s-w")
692 (lambda ()
693 (interactive)
694 (kill-buffer (current-buffer))))
695
696 (exwm-input-set-key
697 (kbd "s-q")
698 (lambda ()
699 (interactive)
700 (exwm-manage--kill-client))))
701 #+end_src
702
703 **** sxhkdrc
704 :PROPERTIES:
705 :header-args+: :tangle ~/.config/sxhkd/sxhkdrc :mkdirp yes
706 :END:
707
708 #+begin_src conf :tangle no
709 # terminal emulator
710 super + Return
711 urxvt
712
713 # program launcher
714 super + space
715 rofi -show run -display-run '> ' -display-window ' 🗔 '
716
717 # window finder
718 super + slash
719 rofi -show window -display-run '> ' -display-window ' 🗔 '
720
721 # password manager
722 alt + space
723 rofi-pass
724
725 # make sxhkd reload its configuration files:
726 super + Escape
727 pkill -USR1 -x sxhkd
728
729 # volume {up,down}
730 XF86Audio{Raise,Lower}Volume
731 pamixer --allow-boost --{in,de}crease 5
732
733 # mute
734 XF86AudioMute
735 pamixer --toggle-mute
736
737 # playback control
738 XF86Audio{Play,Prev,Next}
739 mpc {toggle,prev,next}
740
741 # Toggle keyboard layout
742 # super + F7
743 # toggle-layout
744
745 # Toggle Xfce presentation mode
746 # XF86LaunchB
747 # toggle-presentation-mode
748
749 # monitor brightness
750 XF86MonBrightness{Up,Down}
751 light -{A,U} 5
752
753 super + apostrophe
754 rofi-light
755 #+end_src
756
757 *** [[https://orgmode.org/][Org mode]]
758
759 #+begin_quote
760 Org mode is for keeping notes, maintaining TODO lists, planning
761 projects, and authoring documents with a fast and effective plain-text
762 system.
763 #+end_quote
764
765 In short, my favourite way of life.
766
767 #+begin_src emacs-lisp
768 (setq org-src-tab-acts-natively t
769 org-src-preserve-indentation nil
770 org-edit-src-content-indentation 0)
771 #+end_src
772
773 *** [[https://magit.vc/][Magit]]
774
775 #+begin_quote
776 It's Magit! A Git porcelain inside Emacs.
777 #+end_quote
778
779 Not just how I do git, but /the/ way to do git.
780
781 #+begin_src emacs-lisp
782 (use-package magit
783 :defer t
784 :bind (("s-g" . magit-status)
785 ("C-x g" . magit-status)
786 ("C-x M-g" . magit-dispatch-popup))
787 :config
788 (magit-add-section-hook 'magit-status-sections-hook
789 'magit-insert-modules
790 'magit-insert-stashes
791 'append))
792 #+end_src
793
794 *** [[https://github.com/abo-abo/swiper][Ivy]] (and friends)
795
796 #+begin_quote
797 Ivy - a generic completion frontend for Emacs, Swiper - isearch with
798 an overview, and more. Oh, man!
799 #+end_quote
800
801 There's no way I could top that, so I won't attempt to.
802
803 **** Ivy
804
805 #+begin_src emacs-lisp
806 (use-package ivy
807 :bind
808 (:map ivy-minibuffer-map
809 ([escape] . keyboard-escape-quit)
810 ;; ("C-j" . ivy-next-line)
811 ;; ("C-k" . ivy-previous-line)
812 ([S-up] . ivy-previous-history-element)
813 ([S-down] . ivy-next-history-element)
814 ("DEL" . ivy-backward-delete-char))
815 :config
816 (setq ivy-wrap t)
817 (ivy-mode 1))
818 #+end_src
819
820 **** Swiper
821
822 #+begin_src emacs-lisp
823 (use-package swiper
824 :bind (([remap isearch-forward] . swiper)
825 ([remap isearch-backward] . swiper)))
826 #+end_src
827
828 **** Counsel
829
830 #+begin_src emacs-lisp
831 (use-package counsel
832 :defer 1
833 :bind (([remap execute-extended-command] . counsel-M-x)
834 ([remap find-file] . counsel-find-file)
835 ("s-r" . counsel-recentf)
836 :map minibuffer-local-map
837 ("C-r" . counsel-minibuffer-history))
838 :config
839 (counsel-mode 1)
840 (defalias 'locate #'counsel-locate))
841 #+end_src
842
843 * Borg's =layer/essentials=
844
845 TODO: break this giant source block down into individual org sections.
846
847 #+begin_src emacs-lisp
848 (use-package dash
849 :config (dash-enable-font-lock))
850
851 (use-package diff-hl
852 :config
853 (setq diff-hl-draw-borders nil)
854 (global-diff-hl-mode)
855 (add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh t))
856
857 (use-package dired
858 :defer t
859 :config (setq dired-listing-switches "-alh"))
860
861 (use-package eldoc
862 :when (version< "25" emacs-version)
863 :config (global-eldoc-mode))
864
865 (use-package help
866 :defer t
867 :config (temp-buffer-resize-mode))
868
869 (progn ; `isearch'
870 (setq isearch-allow-scroll t))
871
872 (use-package lisp-mode
873 :config
874 (add-hook 'emacs-lisp-mode-hook 'outline-minor-mode)
875 (add-hook 'emacs-lisp-mode-hook 'reveal-mode)
876 (defun indent-spaces-mode ()
877 (setq indent-tabs-mode nil))
878 (add-hook 'lisp-interaction-mode-hook #'indent-spaces-mode))
879
880 (use-package man
881 :defer t
882 :config (setq Man-width 80))
883
884 (use-package paren
885 :config (show-paren-mode))
886
887 (use-package prog-mode
888 :config (global-prettify-symbols-mode)
889 (defun indicate-buffer-boundaries-left ()
890 (setq indicate-buffer-boundaries 'left))
891 (add-hook 'prog-mode-hook #'indicate-buffer-boundaries-left))
892
893 (use-package recentf
894 :demand t
895 :config (add-to-list 'recentf-exclude "^/\\(?:ssh\\|su\\|sudo\\)?:"))
896
897 (use-package savehist
898 :config (savehist-mode))
899
900 (use-package saveplace
901 :when (version< "25" emacs-version)
902 :config (save-place-mode))
903
904 (use-package simple
905 :config (column-number-mode))
906
907 (progn ; `text-mode'
908 (add-hook 'text-mode-hook #'indicate-buffer-boundaries-left))
909
910 (use-package tramp
911 :defer t
912 :config
913 (add-to-list 'tramp-default-proxies-alist '(nil "\\`root\\'" "/ssh:%h:"))
914 (add-to-list 'tramp-default-proxies-alist '("localhost" nil nil))
915 (add-to-list 'tramp-default-proxies-alist
916 (list (regexp-quote (system-name)) nil nil)))
917
918 (use-package undo-tree
919 :config
920 (global-undo-tree-mode)
921 (setq undo-tree-mode-lighter ""))
922 #+end_src
923
924 * Programming modes
925
926 ** Lean mode
927
928 #+begin_src emacs-lisp
929 (use-package lean-mode
930 :bind (:map lean-mode-map
931 ("S-SPC" . company-complete)))
932 #+end_src
933
934 * Post initialization
935 :PROPERTIES:
936 :CUSTOM_ID: post-initialization
937 :END:
938
939 Display how long it took to load the init file.
940
941 #+begin_src emacs-lisp
942 (message "Loading %s...done (%.3fs)" user-init-file
943 (float-time (time-subtract (current-time)
944 ab--before-user-init-time)))
945 #+end_src
946
947 * Footer
948 :PROPERTIES:
949 :CUSTOM_ID: footer
950 :END:
951
952 #+begin_src emacs-lisp :comments none
953 ;;; init.el ends here
954 #+end_src