]> git.armaanb.net Git - config.org.git/blob - config.org
Add c-mode settings
[config.org.git] / config.org
1 #+TITLE: System Configuration
2 #+DESCRIPTION: Personal system configuration in org-mode format.
3 #+AUTHOR: Armaan Bhojwani
4 #+EMAIL: me@armaanb.net
5
6 * Welcome
7 Welcome to my system configuration! This file contains my Emacs configuration, but also my config files for many of the other programs on my system!
8 ** Compatability
9 I am currently using Emacs 27.2 on Linux, so some settings and packages may not be available for older versions of Emacs. This is a purely personal configuration, so while I can guarantee that it works on my setup, it might not work for you.
10 ** Choices
11 I chose to create a powerful, yet not overly heavy Emacs configuration. Things like a fancy modeline, icons, or LSP mode do not increase my productivity, and create visual clutter, and thus have been excluded.
12
13 Another important choice has been to integrate Emacs into a large part of my computing environment (see [[*Emacs OS]]). I use email, IRC, RSS, et cetera, all through Emacs which simplifies my workflow and creates an amazingly integrated environment.
14
15 Lastly, I use Evil mode. Modal keybindings are simpler and more ergonomic than standard Emacs style, and Vim keybindings are what I'm comfortable with and are pervasive throughout computing.
16 ** License
17 Released under the [[https://opensource.org/licenses/MIT][MIT license]] by Armaan Bhojwani, 2021. Note that many snippets are taken from online, and other sources, who are credited for their work near their contributions.
18 * Package management
19 ** Bootstrap straight.el
20 straight.el is really nice for managing package, and it integrates nicely with use-package. It uses the bootstrapping system defined here for installation.
21 #+begin_src emacs-lisp
22   (defvar native-comp-deferred-compilation-deny-list ())
23   (defvar bootstrap-version)
24   (let ((bootstrap-file
25          (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
26         (bootstrap-version 5))
27     (unless (file-exists-p bootstrap-file)
28       (with-current-buffer
29           (url-retrieve-synchronously
30            "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
31            'silent 'inhibit-cookies)
32         (goto-char (point-max))
33         (eval-print-last-sexp)))
34     (load bootstrap-file nil 'nomessage))
35 #+end_src
36 ** Replace package.el with straight
37 #+begin_src emacs-lisp
38   (straight-use-package 'use-package)
39   (setq straight-use-package-by-default t)
40 #+end_src
41 * Visual options
42 ** Theme
43 Use the Modus Operandi theme by Protesilaos Stavrou. Its the best theme for Emacs by far, because how clear and readable it is. It is highly customizable, but I just set a few options here.
44 #+begin_src emacs-lisp
45   (use-package modus-themes
46     :custom
47     (modus-themes-slanted-constructs t)
48     (modus-themes-bold-constructs t)
49     (modus-themes-mode-line '3d)
50     (modus-themes-scale-headings t)
51     (modus-themes-diffs 'desaturated)
52     :config (load-theme 'modus-vivendi t))
53 #+end_src
54 ** Typography
55 *** Font
56 JetBrains Mono is a great programming font with ligatures. The "NF" means that it has been patched with the [[https://www.nerdfonts.com/][Nerd Fonts]].
57 #+begin_src emacs-lisp
58   (add-to-list 'default-frame-alist '(font . "JetBrainsMonoNF-12"))
59 #+end_src
60 *** Ligatures
61 #+begin_src emacs-lisp
62   (use-package ligature
63     :straight (ligature :type git :host github :repo "mickeynp/ligature.el")
64     :config
65     (ligature-set-ligatures
66      '(prog-mode text-mode)
67      '("-|" "-~" "---" "-<<" "-<" "--" "->" "->>" "-->" "/=" "/=="
68        "/>" "//" "/*" "*>" "*/" "<-" "<<-" "<=>" "<=" "<|" "<||"
69        "<|||" "<|>" "<:" "<>" "<-<" "<<<" "<==" "<<=" "<=<" "<==>"
70        "<-|" "<<" "<~>" "<=|" "<~~" "<~" "<$>" "<$" "<+>" "<+" "</>"
71        "</" "<*" "<*>" "<->" "<!--" ":>" ":<" ":::" "::" ":?" ":?>"
72        ":=" "::=" "=>>" "==>" "=/=" "=!=" "=>" "===" "=:=" "==" "!=="
73        "!!" "!=" ">]" ">:" ">>-" ">>=" ">=>" ">>>" ">-" ">=" "&&&"
74        "&&" "|||>" "||>" "|>" "|]" "|}" "|=>" "|->" "|=" "||-" "|-"
75        "||=" "||" ".." ".?" ".=" ".-" "..<" "..." "+++" "+>" "++"
76        "[||]" "[<" "[|" "{|" "?." "?=" "?:" "##" "###" "####" "#["
77        "#{" "#=" "#!" "#:" "#_(" "#_" "#?" "#(" ";;" "_|_" "__" "~~"
78        "~~>" "~>" "~-" "~@" "$>" "^=" "]#"))
79     (global-ligature-mode t))
80 #+end_src
81 ** Line numbers
82 Display relative line numbers except in certain modes.
83 #+begin_src emacs-lisp
84   (global-display-line-numbers-mode)
85   (setq display-line-numbers-type 'relative)
86   (dolist (no-line-num '(term-mode-hook
87                          pdf-view-mode-hook
88                          shell-mode-hook
89                          org-mode-hook
90                          circe-mode-hook
91                          eshell-mode-hook))
92     (add-hook no-line-num (lambda () (display-line-numbers-mode 0))))
93 #+end_src
94 ** Highlight matching parenthesis
95 #+begin_src emacs-lisp
96   (use-package paren
97     :config (show-paren-mode)
98     :custom (show-paren-style 'parenthesis))
99 #+end_src
100 ** Modeline
101 *** Show current function
102 #+begin_src emacs-lisp
103   (which-function-mode)
104 #+end_src
105 *** Make position in file more descriptive
106 Show current column and file size.
107 #+begin_src emacs-lisp
108   (column-number-mode)
109   (size-indication-mode)
110 #+end_src
111 *** Hide minor modes
112 #+begin_src emacs-lisp
113   (use-package minions
114     :config (minions-mode))
115 #+end_src
116 ** Whitespace mode
117 Highlight whitespace and other bad text practices.
118 #+begin_src emacs-lisp
119   (use-package whitespace
120     :custom (whitespace-style '(face lines-tail)))
121   (dolist (hook '(prog-mode-hook))
122     (add-hook hook (lambda () (whitespace-mode 1))))
123 #+end_src
124 ** Highlight todo items in comments
125 +begin_src emacs-lisp
126   (use-package hl-todo
127     :straight (hl-todo :type git :host github :repo "tarsius/hl-todo")
128     :config (global-hl-todo-mode 1))
129 #+end_src
130 ** Blink cursor
131 #+begin_src emacs-lisp
132   (blink-cursor-mode)
133 #+end_src
134 ** Visual line mode
135 Soft wrap words and do operations by visual lines in some modes.
136 #+begin_src emacs-lisp
137   (dolist (hook '(text-mode-hook
138                   org-mode-hook
139                   markdown-mode-hook
140                   mu4e-view-mode-hook))
141     (add-hook hook (lambda () (visual-line-mode 1))))
142 #+end_src
143 ** Auto fill mode
144 #+begin_src emacs-lisp
145   (dolist (hook '(scdoc-mode-hook
146                   mu4e-compose-mode-hook))
147     (add-hook hook (lambda () (auto-fill-mode 1))))
148 #+end_src
149 ** Display number of matches in search
150 #+begin_src emacs-lisp
151   (use-package anzu
152     :config (global-anzu-mode)
153     :bind
154     ([remap query-replace] . anzu-query-replace)
155     ([remap query-replace-regexp] . anzu-query-replace-regexp))
156 #+end_src
157 *** TODO This config doesn't work right
158 ** Visual bell
159 Invert modeline color instead of audible bell or the standard visual bell.
160 #+begin_src emacs-lisp
161   (setq visible-bell nil
162         ring-bell-function
163         (lambda () (invert-face 'mode-line)
164           (run-with-timer 0.1 nil #'invert-face 'mode-line)))
165 #+end_src
166 * Evil mode
167 ** General
168 #+begin_src emacs-lisp
169   (use-package evil
170     :custom (select-enable-clipboard nil)
171     :config
172     (evil-mode)
173     (fset 'evil-visual-update-x-selection 'ignore) ;; Keep clipboard and register seperate
174     ;; Use visual line motions even outside of visual-line-mode buffers
175     (evil-global-set-key 'motion "j" 'evil-next-visual-line)
176     (evil-global-set-key 'motion "k" 'evil-previous-visual-line)
177     (global-set-key (kbd "<escape>") 'keyboard-escape-quit))
178 #+end_src
179 ** Evil collection
180 Evil bindings for tons of packages.
181 #+begin_src emacs-lisp
182   (use-package evil-collection
183     :after evil
184     :init (evil-collection-init)
185     :custom (evil-collection-setup-minibuffer t))
186 #+end_src
187 ** Surround
188 tpope prevails!
189 #+begin_src emacs-lisp
190   (use-package evil-surround
191     :config (global-evil-surround-mode))
192 #+end_src
193 ** Nerd commenter
194 Makes commenting super easy
195 #+begin_src emacs-lisp
196   (use-package evil-nerd-commenter
197     :bind (:map evil-normal-state-map
198                 ("gc" . evilnc-comment-or-uncomment-lines))
199     :custom (evilnc-invert-comment-line-by-line nil))
200 #+end_src
201 ** Undo redo
202 Fix the oopsies!
203 #+begin_src emacs-lisp
204   (use-package undo-fu
205     :config (evil-set-undo-system 'undo-fu))
206
207   (use-package undo-fu-session
208     :config (global-undo-fu-session-mode))
209 #+end_src
210 ** Number incrementing
211 Add back C-a/C-x bindings.
212 #+begin_src emacs-lisp
213   (use-package evil-numbers
214     :straight (evil-numbers :type git :host github :repo "juliapath/evil-numbers")
215     :bind (:map evil-normal-state-map
216                 ("C-M-a" . evil-numbers/inc-at-pt)
217                 ("C-M-x" . evil-numbers/dec-at-pt)))
218 #+end_src
219 ** Evil org
220 #+begin_src emacs-lisp
221   (use-package evil-org
222     :after org
223     :hook (org-mode . evil-org-mode)
224     :config
225     (evil-org-set-key-theme '(textobjects insert navigation shift todo)))
226
227   (use-package evil-org-agenda
228     :straight (:type built-in)
229     :after evil-org
230     :config (evil-org-agenda-set-keys))
231 #+end_src
232 * Org mode
233 ** General
234 #+begin_src emacs-lisp
235   (use-package org
236     :straight (:type built-in)
237     :commands (org-capture org-agenda)
238     :custom
239     (org-ellipsis " ▾")
240     (org-agenda-start-with-log-mode t)
241     (org-agenda-files (quote ("~/org/tasks.org")))
242     (org-log-done 'time)
243     (org-log-into-drawer t)
244     (org-src-tab-acts-natively t)
245     (org-src-fontify-natively t)
246     (org-startup-indented t)
247     (org-hide-emphasis-markers t)
248     (org-fontify-whole-block-delimiter-line nil)
249     (org-archive-default-command 'org-archive-to-archive-sibling)
250     :bind
251     ("C-c a" . org-agenda)
252     (:map evil-normal-state-map ("ga" . org-archive-subtree-default)))
253 #+end_src
254 ** Tempo
255 Define templates for lots of common structure elements. Mostly just used within this file.
256 #+begin_src emacs-lisp
257   (use-package org-tempo
258     :after org
259     :straight (:type built-in)
260     :config
261     (dolist (addition '(("el" . "src emacs-lisp")
262                         ("el" . "src emacs-lisp")
263                         ("sp" . "src conf :tangle ~/.spectrwm.conf")
264                         ("ash" . "src shell :tangle ~/.config/ash/ashrc")
265                         ("pi" . "src conf :tangle ~/.config/picom/picom.conf")
266                         ("git" . "src conf :tangle ~/.gitconfig")
267                         ("za" . "src conf :tangle ~/.config/zathura/zathurarc")
268                         ("xr" . "src conf :tangle ~/.Xresources")
269                         ("tm" . "src conf :tangle ~/.tmux.conf")
270                         ("gp" . "src conf :tangle ~/.gnupg/gpg.conf")
271                         ("ag" . "src conf :tangle ~/.gnupg/gpg-agent.conf")
272                         ("xm" . "src conf :tangle ~/.config/xmodmap")))
273       (add-to-list 'org-structure-template-alist addition)))
274 #+end_src
275 * Autocompletion
276 ** Ivy
277 A well balanced completion framework.
278 #+begin_src emacs-lisp
279   (use-package ivy
280     :bind (:map ivy-minibuffer-map
281            ("TAB" . ivy-alt-done))
282            (:map ivy-switch-buffer-map
283            ("M-d" . ivy-switch-buffer-kill))
284     :config (ivy-mode))
285 #+end_src
286 ** Ivy-rich
287 #+begin_src emacs-lisp
288   (use-package ivy-rich
289     :after (ivy counsel)
290     :config (ivy-rich-mode))
291 #+end_src
292 ** Counsel
293 Ivy everywhere.
294 #+begin_src emacs-lisp
295   (use-package counsel
296     :bind ("C-M-j" . 'counsel-switch-buffer)
297     :config (counsel-mode))
298 #+end_src
299 ** Remember frequent commands
300 #+begin_src emacs-lisp
301   (use-package ivy-prescient
302     :after counsel
303     :config
304     (prescient-persist-mode)
305     (ivy-prescient-mode))
306 #+end_src
307 * Emacs OS
308 ** RSS
309 Use elfeed for reading RSS. I have another file with all the feeds in it that I'd rather keep private.
310 #+begin_src emacs-lisp
311   (use-package elfeed
312     :bind (("C-c e" . elfeed))
313     :config (load "~/.emacs.d/feeds.el")
314     :bind (:map elfeed-search-mode-map ("C-c C-o" . 'elfeed-show-visit)))
315 #+end_src
316 ** Email
317 Use mu4e for reading emails.
318 Contexts are a not very well known feature of mu4e that makes it super easy to manage multiple accounts. Much better than some of the hacky methods and external packages that I've seen.
319 *** TODO Switch to mbsync
320 =offlineimap= really crappy (slow and requires python2), and I need to replace it with =mbsync=.
321 *** TODO Include mbsync/offlineimap config
322 *** TODO Split up and document this config a bit
323 *** Setup
324 #+begin_src emacs-lisp
325   (use-package smtpmail
326     :straight (:type built-in))
327   (use-package mu4e
328     :load-path "/usr/share/emacs/site-lisp/mu4e"
329     :straight (:build nil)
330     :bind (("C-c m" . mu4e))
331     :config
332     (setq user-full-name "Armaan Bhojwani"
333           smtpmail-local-domain "armaanb.net"
334           smtpmail-stream-type 'ssl
335           smtpmail-smtp-service '465
336           mu4e-change-filenames-when-moving t
337           mu4e-get-mail-command "offlineimap -q"
338           message-citation-line-format "On %a %d %b %Y at %R, %f wrote:\n"
339           message-citation-line-function 'message-insert-formatted-citation-line
340           mu4e-completing-read-function 'ivy-completing-read
341           mu4e-confirm-quit nil
342           mu4e-view-use-gnus t
343           mail-user-agent 'mu4e-user-agent
344           mu4e-context-policy 'pick-first
345           mu4e-contexts
346           `( ,(make-mu4e-context
347                :name "school"
348                :enter-func (lambda () (mu4e-message "Entering school context"))
349                :leave-func (lambda () (mu4e-message "Leaving school context"))
350                :match-func (lambda (msg)
351                              (when msg
352                                (string-match-p "^/school" (mu4e-message-field msg :maildir))))
353                :vars '((user-mail-address . "abhojwani22@nobles.edu")
354                        (mu4e-sent-folder . "/school/Sent")
355                        (mu4e-drafts-folder . "/school/Drafts")
356                        (mu4e-trash-folder . "/school/Trash")
357                        (mu4e-refile-folder . "/school/Archive")
358                        (message-cite-reply-position . above)
359                        (user-mail-address . "abhojwani22@nobles.edu")
360                        (smtpmail-smtp-user . "abhojwani22@nobles.edu")
361                        (smtpmail-smtp-server . "smtp.gmail.com")))
362              ,(make-mu4e-context
363                :name "personal"
364                :enter-func (lambda () (mu4e-message "Entering personal context"))
365                :leave-func (lambda () (mu4e-message "Leaving personal context"))
366                :match-func (lambda (msg)
367                              (when msg
368                                (string-match-p "^/personal" (mu4e-message-field msg :maildir))))
369                :vars '((mu4e-sent-folder . "/personal/Sent")
370                        (mu4e-drafts-folder . "/personal/Drafts")
371                        (mu4e-trash-folder . "/personal/Trash")
372                        (mu4e-refile-folder . "/personal/Archive")
373                        (user-mail-address . "me@armaanb.net")
374                        (message-cite-reply-position . below)
375                        (smtpmail-smtp-user . "me@armaanb.net")
376                        (smtpmail-smtp-server . "smtp.mailbox.org")))))
377     (add-to-list 'mu4e-bookmarks
378                  '(:name "Unified inbox"
379                          :query "maildir:\"/personal/INBOX\" or maildir:\"/school/INBOX\""
380                          :key ?b))
381     :hook ((mu4e-compose-mode . flyspell-mode)
382            (message-send-hook . (lambda () (unless (yes-or-no-p "Ya sure 'bout that?")
383                                              (signal 'quit nil))))))
384 #+end_src
385 *** Discourage Gnus from displaying HTML emails
386 #+begin_src emacs-lisp
387   (with-eval-after-load "mm-decode"
388     (add-to-list 'mm-discouraged-alternatives "text/html")
389     (add-to-list 'mm-discouraged-alternatives "text/richtext"))
390 #+end_src
391 ** Default browser
392 Set EWW as default browser except for multimedia which should open in MPV.
393 #+begin_src emacs-lisp
394   (defun browse-url-mpv (url &optional new-window)
395     "Ask MPV to load URL."
396     (interactive)
397     (start-process "mpv" "*mpv*" "mpv" url))
398
399   (setq browse-url-handlers
400         (quote
401          (("youtu\\.?be" . browse-url-mpv)
402           ("peertube.*" . browse-url-mpv)
403           ("vid.*" . browse-url-mpv)
404           ("vid.*" . browse-url-mpv)
405           ("*.mp4" . browse-url-mpv)
406           ("*.mp3" . browse-url-mpv)
407           ("*.ogg" . browse-url-mpv)
408           ("." . eww-browse-url)
409           )))
410 #+end_src
411 ** EWW
412 Some EWW enhancements.
413 *** Give buffer a useful name
414 #+begin_src emacs-lisp
415   ;; From https://protesilaos.com/dotemacs/
416   (defun prot-eww--rename-buffer ()
417     "Rename EWW buffer using page title or URL.
418         To be used by `eww-after-render-hook'."
419     (let ((name (if (eq "" (plist-get eww-data :title))
420                     (plist-get eww-data :url)
421                   (plist-get eww-data :title))))
422       (rename-buffer (format "*%s # eww*" name) t)))
423
424   (use-package eww
425     :straight (:type built-in)
426     :bind (("C-c w" . eww))
427     :hook (eww-after-render-hook prot-eww--rename-buffer))
428 #+end_src
429 *** Keybinding
430 #+begin_src emacs-lisp
431   (global-set-key (kbd "C-c w") 'eww)
432 #+end_src
433 ** IRC
434 Circe is a really nice IRC client that claims to be above RCIRC and below ERC in terms of features. ERC felt a bit messy and finicky to me, and Circe has all the features that I need. This setup gets the password for my bouncer (Pounce) instances via my =~/.authinfo.gpg= file.
435 #+begin_src emacs-lisp
436   (defun fetch-password (&rest params)
437     (require 'auth-source)
438     (let ((match (car (apply 'auth-source-search params))))
439       (if match
440           (let ((secret (plist-get match :secret)))
441             (if (functionp secret)
442                 (funcall secret)
443               secret))
444         (error "Password not found for %S" params))))
445
446   (use-package circe
447     :config
448     (enable-lui-track)
449     (enable-circe-color-nicks)
450     (setq circe-network-defaults '(("libera"
451                                     :host "irc.armaanb.net"
452                                     :nick "emacs"
453                                     :user "emacs"
454                                     :use-tls t
455                                     :port 6698
456                                     :pass (lambda (null) (fetch-password
457                                                           :login "emacs"
458                                                           :machine "irc.armaanb.net"
459                                                           :port 6698)))
460                                    ("oftc"
461                                     :host "irc.armaanb.net"
462                                     :nick "emacs"
463                                     :user "emacs"
464                                     :use-tls t
465                                     :port 6699
466                                     :pass (lambda (null) (fetch-password
467                                                           :login "emacs"
468                                                           :machine "irc.armaanb.net"
469                                                           :port 6699)))
470                                    ("tilde"
471                                     :host "irc.armaanb.net"
472                                     :nick "emacs"
473                                     :user "emacs"
474                                     :use-tls t
475                                     :port 6696
476                                     :pass (lambda (null) (fetch-password
477                                                           :login "emacs"
478                                                           :machine "irc.armaanb.net"
479                                                           :port 6696)))))
480     :custom (circe-default-part-message "goodbye!")
481     :bind (:map circe-mode-map ("C-c C-r" . circe-reconnect-all)))
482
483   (defun acheam-irc ()
484     "Open circe"
485     (interactive)
486     (if (get-buffer "irc.armaanb.net:6696")
487         (switch-to-buffer "irc.armaanb.net:6696")
488       (progn (switch-to-buffer "*scratch*")
489              (circe "libera")
490              (circe "oftc")
491              (circe "tilde"))))
492
493   (global-set-key (kbd "C-c i") 'acheam-irc)
494 #+end_src
495 ** Calendar
496 Still experimenting with this setup. Not sure if I will keep it, but it works well for seeing my calendar events. I use =vdirsyncer= to sync my calendar events which I'm really not happy with.
497 #+begin_src emacs-lisp
498   (defun sync-calendar ()
499     "Sync calendars with vdirsyncer"
500     (interactive)
501     (async-shell-command "vdirsyncer sync"))
502
503   (use-package calfw
504     :bind (:map cfw:calendar-mode-map ("C-S-u" . sync-calendar)))
505   (use-package calfw-ical)
506   (use-package calfw-org)
507
508   (defun acheam-calendar ()
509     "Open calendars"
510     (interactive)
511     (cfw:open-calendar-buffer
512      :contents-sources (list
513                         (cfw:org-create-source "Green")
514                         (cfw:ical-create-source
515                          "Personal"
516                          "~/.local/share/vdirsyncer/mailbox/Y2FsOi8vMC8zMQ.ics"
517                          "Gray")
518                         (cfw:ical-create-source
519                          "Personal"
520                          "~/.local/share/vdirsyncer/mailbox/Y2FsOi8vMC8zMQ.ics"
521                          "Red")
522                         (cfw:ical-create-source
523                          "School"
524                          "~/.local/share/vdirsyncer/school/abhojwani22@nobles.edu.ics"
525                          "Cyan"))
526      :view 'week))
527
528   (global-set-key (kbd "C-c c") 'acheam-calendar)
529 #+end_src
530 ** PDF reader
531 #+begin_src emacs-lisp
532   (use-package pdf-tools
533     :hook (pdf-view-mode . pdf-view-midnight-minor-mode))
534 #+end_src
535 * Emacs IDE
536 ** Python formatting
537 #+begin_src emacs-lisp
538   (use-package blacken
539     :hook (python-mode . blacken-mode)
540     :custom (blacken-line-length 79))
541
542 #+end_src
543 ** Strip trailing whitespace
544 #+begin_src emacs-lisp
545   (use-package ws-butler
546     :config (ws-butler-global-mode))
547 #+end_src
548 ** Flycheck
549 Automatic linting. I need to look into configuring this more.
550 #+begin_src emacs-lisp
551   (use-package flycheck
552     :config (global-flycheck-mode))
553 #+end_src
554 ** Project management
555 I never use this, but apparently its very powerful. Another item on my todo list.
556 #+begin_src emacs-lisp
557   (use-package projectile
558     :config (projectile-mode)
559     :custom ((projectile-completion-system 'ivy))
560     :bind-keymap
561     ("C-c p" . projectile-command-map)
562     :init
563     (when (file-directory-p "~/src")
564       (setq projectile-project-search-path '("~/src")))
565     (setq projectile-switch-project-action #'projectile-dired))
566
567   (use-package counsel-projectile
568     :after projectile
569     :config (counsel-projectile-mode))
570 #+end_src
571 ** Dired
572 The best file manager!
573 #+begin_src emacs-lisp
574   (use-package dired
575     :straight (:type built-in)
576     :commands (dired dired-jump)
577     :custom ((dired-listing-switches "-agho --group-directories-first"))
578     :config (evil-collection-define-key 'normal 'dired-mode-map
579               "h" 'dired-single-up-directory
580               "l" 'dired-single-buffer))
581
582   (use-package dired-single
583     :commands (dired dired-jump))
584
585   (use-package dired-open
586     :commands (dired dired-jump)
587     :custom (dired-open-extensions '(("png" . "feh")
588                                      ("mkv" . "mpv"))))
589
590   (use-package dired-hide-dotfiles
591     :hook (dired-mode . dired-hide-dotfiles-mode)
592     :config
593     (evil-collection-define-key 'normal 'dired-mode-map
594       "H" 'dired-hide-dotfiles-mode))
595 #+end_src
596 ** Git
597 *** Magit
598 A very good Git interface.
599 #+begin_src emacs-lisp
600   (use-package magit)
601 #+end_src
602 *** Email
603 #+begin_src emacs-lisp
604   (use-package piem)
605   (use-package git-email
606     :straight (git-email :repo "https://git.sr.ht/~yoctocell/git-email")
607     :config (git-email-piem-mode))
608 #+end_src
609 ** C
610 Modified from https://eklitzke.org/smarter-emacs-clang-format.
611
612 Style is basically ddevault's style guide but with 4 spaces instead of 8 char tabs.
613 #+begin_src emacs-lisp
614   (use-package clang-format
615     :custom (clang-format-style "{
616         BasedOnStyle: llvm,
617         AlwaysBreakAfterReturnType: AllDefinitions,
618         IndentWidth: 4,
619         }"))
620
621   (defun clang-format-buffer-smart ()
622     "Reformat buffer if .clang-format exists in the projectile root."
623     (when (file-exists-p (expand-file-name ".clang-format" (projectile-project-root)))
624       (when (if (eq major-mode 'c-mode))
625         (message "Formatting with clang-format...")
626         (clang-format-buffer))))
627
628   (add-hook 'before-save-hook 'clang-format-buffer-smart nil)
629 #+end_src
630 * General text editing
631 ** Spell checking
632 Spell check in text mode, and in prog-mode comments.
633 #+begin_src emacs-lisp
634   (dolist (hook '(text-mode-hook
635                   markdown-mode-hook
636                   scdoc-mode-hook))
637     (add-hook hook (lambda () (flyspell-mode))))
638   (dolist (hook '(change-log-mode-hook log-edit-mode-hook))
639     (add-hook hook (lambda () (flyspell-mode -1))))
640   (add-hook 'prog-mode (lambda () (flyspell-prog mode)))
641   (setq ispell-silently-savep t)
642 #+end_src
643 ** Sane tab width
644 #+begin_src emacs-lisp
645   (setq-default tab-width 2)
646 #+end_src
647 ** Save place
648 Opens file where you left it.
649 #+begin_src emacs-lisp
650   (save-place-mode)
651 #+end_src
652 ** Writing mode
653 Distraction free writing a la junegunn/goyo.
654 #+begin_src emacs-lisp
655   (use-package olivetti
656     :bind ("C-c o" . olivetti-mode))
657 #+end_src
658 ** Abbreviations
659 Abbreviate things! I just use this for things like my email address and copyright notice.
660 #+begin_src emacs-lisp
661   (setq abbrev-file-name "~/.emacs.d/abbrevs.el")
662   (setq save-abbrevs 'silent)
663   (setq-default abbrev-mode t)
664 #+end_src
665 ** TRAMP
666 #+begin_src emacs-lisp
667   (setq tramp-default-method "ssh")
668 #+end_src
669 ** Follow version controlled symlinks
670 #+begin_src emacs-lisp
671   (setq vc-follow-symlinks t)
672 #+end_src
673 ** Open file as root
674 #+begin_src emacs-lisp
675   (defun doas-edit (&optional arg)
676     "Edit currently visited file as root.
677
678     With a prefix ARG prompt for a file to visit.
679     Will also prompt for a file to visit if current
680     buffer is not visiting a file.
681
682     Modified from Emacs Redux."
683     (interactive "P")
684     (if (or arg (not buffer-file-name))
685         (find-file (concat "/doas:root@localhost:"
686                            (ido-read-file-name "Find file(as root): ")))
687       (find-alternate-file (concat "/doas:root@localhost:" buffer-file-name))))
688
689     (global-set-key (kbd "C-x C-r") #'doas-edit)
690 #+end_src
691 ** Markdown mode
692 #+begin_src emacs-lisp
693   (use-package markdown-mode)
694 #+end_src
695 ** scdoc mode
696 Get it for yourself at https://git.armaanb.net/scdoc
697 #+begin_src emacs-lisp
698   (add-to-list 'load-path "~/src/scdoc-mode")
699   (autoload 'scdoc-mode "scdoc-mode" "Major mode for editing scdoc files" t)
700   (add-to-list 'auto-mode-alist '("\\.scd\\'" . scdoc-mode))
701 #+end_src
702 * Keybindings
703 ** Switch windows
704 #+begin_src emacs-lisp
705   (use-package ace-window
706     :bind ("M-o" . ace-window))
707 #+end_src
708 ** Kill current buffer
709 Makes "C-x k" binding faster.
710 #+begin_src emacs-lisp
711   (substitute-key-definition 'kill-buffer 'kill-buffer-and-window global-map)
712 #+end_src
713 * Other settings
714 ** OpenSCAD syntax
715 #+begin_src emacs-lisp
716   (use-package scad-mode)
717 #+end_src
718 ** Control backup and lock files
719 Stop backup files from spewing everywhere.
720 #+begin_src emacs-lisp
721   (setq backup-directory-alist `(("." . "~/.emacs.d/backups"))
722         create-lockfiles nil)
723 #+end_src
724 ** Make yes/no easier
725 #+begin_src emacs-lisp
726   (defalias 'yes-or-no-p 'y-or-n-p)
727 #+end_src
728 ** Move customize file
729 No more clogging up init.el.
730 #+begin_src emacs-lisp
731   (setq custom-file "~/.emacs.d/custom.el")
732   (load custom-file)
733 #+end_src
734 ** Better help
735 #+begin_src emacs-lisp
736   (use-package helpful
737     :commands (helpful-callable helpful-variable helpful-command helpful-key)
738     :custom
739     (counsel-describe-function-function #'helpful-callable)
740     (counsel-describe-variable-function #'helpful-variable)
741     :bind
742     ([remap describe-function] . counsel-describe-function)
743     ([remap describe-command] . helpful-command)
744     ([remap describe-variable] . counsel-describe-variable)
745     ([remap describe-key] . helpful-key))
746 #+end_src
747 ** GPG
748 #+begin_src emacs-lisp
749   (use-package epa-file
750     :straight (:type built-in)
751     :custom
752     (epa-file-select-keys nil)
753     (epa-file-encrypt-to '("me@armaanb.net"))
754     (password-cache-expiry (* 60 15)))
755
756   (use-package pinentry
757     :config (pinentry-start))
758 #+end_src
759 ** Pastebin
760 #+begin_src emacs-lisp
761   (use-package 0x0
762     :straight (0x0 :type git :repo "https://git.sr.ht/~zge/nullpointer-emacs")
763     :custom (0x0-default-service 'envs))
764 #+end_src
765 *** TODO Replace this with uploading to my own server
766 Similar to the ufile alias in my ashrc
767 ** Automatically clean buffers
768 Automatically close unused buffers (except those of Circe) at midnight.
769 #+begin_src emacs-lisp
770   (midnight-mode)
771   (add-to-list 'clean-buffer-list-kill-never-regexps (lambda (buffer-name)
772                                                        (with-current-buffer buffer-name
773                                                          (derived-mode-p 'lui-mode))))
774 #+end_src
775 * Tangles
776 ** Spectrwm
777 Spectrwm is a really awesome window manager! Would highly recommend.
778 *** General settings
779 #+begin_src conf :tangle ~/.spectrwm.conf
780   workspace_limit = 5
781   warp_pointer = 1
782   modkey = Mod4
783   autorun = ws[1]:/home/armaa/src/bin/autostart
784 #+end_src
785 *** Bar
786 Disable the bar by default (it can still be brought back up with MOD+b). The font just needs to be set to something that you have installed, otherwise spectrwm won't launch.
787 #+begin_src conf :tangle ~/.spectrwm.conf
788   bar_enabled = 0
789   bar_font = xos4 JetBrains Mono:pixelsize=14:antialias=true # any installed font
790 #+end_src
791 *** Keybindings
792 I'm not a huge fan of how spectrwm handles keybindings, probably my biggest gripe with it.
793 **** WM actions
794 #+begin_src conf :tangle ~/.spectrwm.conf
795   program[term] = st -e tmux
796   program[notif] = /home/armaa/src/bin/setter status
797   program[pass] = /home/armaa/src/bin/passmenu
798   program[lock] = slock
799
800   bind[notif] = MOD+n
801   bind[pass] = MOD+Shift+p
802 #+end_src
803 **** Media keys
804 #+begin_src conf :tangle ~/.spectrwm.conf
805   program[paup] = /home/armaa/src/bin/setter audio +5
806   program[padown] = /home/armaa/src/bin/setter audio -5
807   program[pamute] = /home/armaa/src/bin/setter audio
808   program[brigup] = /home/armaa/src/bin/setter brightness +10%
809   program[brigdown] = /home/armaa/src/bin/setter brightness 10%-
810
811   bind[padown] = XF86AudioLowerVolume
812   bind[paup] = XF86AudioRaiseVolume
813   bind[pamute] = XF86AudioMute
814   bind[brigdown] = XF86MonBrightnessDown
815   bind[brigup] = XF86MonBrightnessUp
816 #+end_src
817 **** HJKL
818 #+begin_src conf :tangle ~/.spectrwm.conf
819   program[h] = xdotool keyup h key --clearmodifiers Left
820   program[j] = xdotool keyup j key --clearmodifiers Down
821   program[k] = xdotool keyup k key --clearmodifiers Up
822   program[l] = xdotool keyup l key --clearmodifiers Right
823
824   bind[h] = MOD + Control + h
825   bind[j] = MOD + Control + j
826   bind[k] = MOD + Control + k
827   bind[l] = MOD + Control + l
828 #+end_src
829 **** Programs
830 #+begin_src conf :tangle ~/.spectrwm.conf
831   program[email] = emacsclient -ce '(progn (switch-to-buffer "*scratch*") (mu4e))'
832   program[irc] = emacsclient -ce '(acheam-irc)'
833   program[rss] = emacsclient -ce '(elfeed)'
834   program[calendar] = emacsclient -ce '(acheam-calendar)'
835   program[calc] = emacsclient -ce '(progn (calc) (windmove-up) (delete-window))'
836   program[emacs] = emacsclient -c
837   program[bookmarks] = /home/armaa/src/bin/bookmarks
838   program[browser] = chorizo
839
840   bind[email] = MOD+Control+1
841   bind[irc] = MOD+Control+2
842   bind[rss] = MOD+Control+3
843   bind[calendar] = MOD+Control+4
844   bind[calc] = MOD+Control+5
845   bind[emacs] = MOD+Control+Return
846   bind[bookmarks] = MOD+Control+o
847   bind[browser] = MOD+Control+p
848 #+end_src
849 *** Quirks
850 Float some specific programs by default.
851 #+begin_src conf :tangle ~/.spectrwm.conf
852   quirk[Castle Menu] = FLOAT
853   quirk[momen] = FLOAT
854   quirk[pinentry-fltk] = FLOAT
855 #+end_src
856 ** Ash
857 *** Options
858 Use the vi editing mode. I still haven't found a good way to show visual feedback of the current mode. Ideally the cursor would change to a beam when in insert mode, and a box when in normal mode.
859 #+begin_src conf :tangle ~/.config/ash/ashrc
860   set -o vi
861 #+end_src
862 *** Functions
863 **** Finger
864 #+begin_src shell :tangle ~/.config/ash/ashrc
865   finger() {
866       user=$(echo "$1" | cut -f 1 -d '@')
867       host=$(echo "$1" | cut -f 2 -d '@')
868       echo $user | nc "$host" 79
869   }
870 #+end_src
871 **** Upload to ftp.armaanb.net
872 #+begin_src shell :tangle ~/.config/ash/ashrc
873   _uprint() {
874       echo "https://l.armaanb.net/$(basename "$1")" | tee /dev/tty | xclip -sel c
875   }
876
877   _uup() {
878       rsync "$1" "armaa@armaanb.net:/srv/ftp/pub/$2" --chmod 644 --progress
879   }
880
881   ufile() {
882       _uup "$1" "$2"
883       _uprint "$1"
884   }
885
886   uclip() {
887       tmp=$(mktemp)
888       xclip -o -sel c >> "$tmp"
889       basetmp=$(echo "$tmp" | tail -c +5)
890       _uup "$tmp" "$basetmp"
891       _uprint "$basetmp"
892       rm -f "$tmp"
893   }
894 #+end_src
895 *** Exports
896 #+begin_src shell :tangle ~/.config/ash/ashrc
897   export EDITOR="emacsclient -c"
898   export VISUAL="$EDITOR"
899   export TERM=xterm-256color # for compatability
900
901   export GPG_TTY="$(tty)"
902   export MANPAGER='nvim +Man!'
903   export PAGER='less'
904   export GTK_USE_PORTAL=1
905   export CDPATH=:~
906
907   export PATH="/home/armaa/.local/bin:$PATH" # prioritize .local/bin
908   export PATH="/home/armaa/src/bin:$PATH" # prioritize my bin
909   export PATH="//home/armaa/src/bin/bin:$PATH" # prioritize my bins
910   export PATH="$PATH:/usr/sbin"
911
912   export LC_ALL="en_US.UTF-8"
913   export LC_CTYPE="en_US.UTF-8"
914   export LANGUAGE="en_US.UTF-8"
915   export TZ="America/New_York"
916
917   export KISS_PATH=""
918   export KISS_PATH="$KISS_PATH:$HOME/repos/personal"
919   export KISS_PATH="$KISS_PATH:$HOME/repos/bin/bin"
920   export KISS_PATH="$KISS_PATH:$HOME/repos/main/core"
921   export KISS_PATH="$KISS_PATH:$HOME/repos/main/extra"
922   export KISS_PATH="$KISS_PATH:$HOME/repos/main/xorg"
923   export KISS_PATH="$KISS_PATH:$HOME/repos/community/community"
924   export KISS_PATH="$KISS_PATH:$HOME/repos/mid/ports"
925
926   export CFLAGS="-O3 -pipe -march=native"
927   export CXXFLAGS="$CFLAGS"
928   export MAKEFLAGS="-j$(nproc)"
929   export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig"
930
931   export XDG_DESKTOP_DIR="/dev/null"
932   export XDG_DOCUMENTS_DIR="$HOME/documents"
933   export XDG_DOWNLOAD_DIR="$HOME/downloads"
934   export XDG_MUSIC_DIR="$HOME/music"
935   export XDG_PICTURES_DIR="$HOME/pictures"
936   export XDG_VIDEOS_DIR="$HOME/videos"
937 #+end_src
938 *** Aliases
939 **** SSH
940 #+begin_src shell :tangle ~/.config/ash/ashrc
941   alias poki='ssh armaanb.net'
942   alias irc='ssh root@armaanb.net -t abduco -A irc catgirl freenode'
943   alias union='ssh 192.168.1.18'
944   alias mine='ssh -p 23 root@pickupserver.cc'
945   alias tcf='ssh root@204.48.23.68'
946   alias ngmun='ssh root@157.245.89.25'
947   alias prox='ssh root@192.168.1.224'
948   alias ncq='ssh root@143.198.123.17'
949   alias envs='ssh acheam@envs.net'
950 #+end_src
951 **** File management
952 #+begin_src shell :tangle ~/.config/ash/ashrc
953   alias ls='LC_COLLATE=C ls -lh --group-directories-first'
954   alias la='ls -A'
955   alias df='df -h / /boot'
956   alias du='du -h'
957   alias free='free -m'
958   alias cp='cp -riv'
959   alias rm='rm -iv'
960   alias mv='mv -iv'
961   alias ln='ln -v'
962   alias grep='grep -in'
963   alias mkdir='mkdir -pv'
964   alias lanex='java -jar ~/.local/share/lxc/lanxchange.jar && rm lxc*'
965   emacs() { $EDITOR "$@" & }
966   alias vim="emacs"
967 #+end_src
968 **** System management
969 #+begin_src shell :tangle ~/.config/ash/ashrc
970   alias crontab='crontab-argh'
971   alias sudo='doas'
972   alias pasu='git -C ~/.password-store push'
973   alias yadu='yadm add -u && yadm commit -m "Updated `date -Iseconds`" && \
974     yadm push'
975 #+end_src
976 **** Networking
977 #+begin_src shell :tangle ~/.config/ash/ashrc
978   alias ping='ping -c 10'
979   alias gps='gpg --keyserver keyserver.ubuntu.com --search-keys'
980   alias gpp='gpg --keyserver keyserver.ubuntu.com --recv-key'
981   alias plan='T=$(mktemp) && \
982           rsync armaanb.net:/home/armaa/plan.txt "$T" && \
983           TT=$(mktemp) && \
984           head -n -2 $T > $TT && \
985           /bin/nvim $TT && \
986           echo >> "$TT" && \
987           echo "Last updated: $(date -R)" >> "$TT" && \
988           fold -sw 72 "$TT" > "$T"| \
989           rsync "$T" armaanb.net:/home/armaa/plan.txt && \
990           rm -f "$T"'
991 #+end_src
992 **** Virtual machines, chroots
993 #+begin_src shell :tangle ~/.config/ash/ashrc
994   alias ckiss="doas chrooter ~/Virtual/kiss"
995   alias cdebian="doas chrooter ~/Virtual/debian bash"
996   alias cwindows='devour qemu-system-x86_64 \
997     -smp 3 \
998     -cpu host \
999     -enable-kvm \
1000     -m 3G \
1001     -device VGA,vgamem_mb=64 \
1002     -device intel-hda \
1003     -device hda-duplex \
1004     -net nic \
1005     -net user,smb=/home/armaa/Public \
1006     -drive format=qcow2,file=/home/armaa/Virtual/windows.qcow2'
1007 #+end_src
1008 **** Latin
1009 #+begin_src shell :tangle ~/.config/ash/ashrc
1010   alias words='gen-shell -c "words"'
1011   alias words-e='gen-shell -c "words ~E"'
1012 #+end_src
1013 **** Other
1014 #+begin_src shell :tangle ~/.config/ash/ashrc
1015   alias bigrandomfile='dd if=/dev/urandom of=1GB-urandom bs=1M count=1024 \
1016     iflag=fullblock'
1017   alias bigboringfile='dd if=/dev/zero of=1GB-zero bs=1M count=1024 \
1018     iflag=fullblock'
1019   alias ytmusic="youtube-dl -x --add-metadata  --audio-format aac \
1020     --restrict-filenames -o '%(title)s.%(ext)s'"
1021   alias bc='bc -l'
1022 #+end_src
1023 ** MPV
1024 Make MPV play a little bit smoother.
1025 #+begin_src conf :tangle ~/.config/mpv/mpv.conf
1026   ytdl-format="bestvideo[height<=?1080]+bestaudio/best"
1027   hwdec=auto-copy
1028 #+end_src
1029 ** Inputrc
1030 This file is used for any GNU Readline programs. I use Emacs editing mode mostly because of one annoyance which is that to clear the screen using ^L, you have to be in normal mode which is a pain. If there is a way to rebind this, I'd love to know!.
1031 #+begin_src conf :tangle ~/.inputrc
1032   set editing-mode emacs
1033 #+end_src
1034 ** Git
1035 *** User
1036 #+begin_src conf :tangle ~/.gitconfig
1037   [user]
1038   name = Armaan Bhojwani
1039   email = me@armaanb.net
1040   signingkey = 0FEB9471E19C49C60CFBEB133C9ED82FFE788E4A
1041 #+end_src
1042 *** Init
1043 #+begin_src conf :tangle ~/.gitconfig
1044   [init]
1045   defaultBranch = main
1046 #+end_src
1047 *** GPG
1048 #+begin_src conf :tangle ~/.gitconfig
1049   [gpg]
1050   program = gpg
1051 #+end_src
1052 *** Sendemail
1053 #+begin_src conf :tangle ~/.gitconfig
1054   [sendemail]
1055   smtpserver = smtp.mailbox.org
1056   smtpuser = me@armaanb.net
1057   smtpencryption = ssl
1058   smtpserverport = 465
1059   confirm = auto
1060 #+end_src
1061 *** Submodule
1062 #+begin_src conf :tangle ~/.gitconfig
1063   [submodule]
1064   recurse = true
1065 #+end_src
1066 *** Aliases
1067 #+begin_src conf :tangle ~/.gitconfig
1068   [alias]
1069   stat = diff --stat
1070   sclone = clone --depth 1
1071   sclean = clean -dfX
1072   a = add
1073   aa = add .
1074   c = commit
1075   quickfix = commit . --amend --no-edit
1076   p = push
1077   subup = submodule update --remote
1078   loc = diff --stat 4b825dc642cb6eb9a060e54bf8d69288fbee4904 # Empty hash
1079   pushnc = push -o skip-ci
1080 #+end_src
1081 *** Commit
1082 #+begin_src conf :tangle ~/.gitconfig
1083   [commit]
1084   gpgsign = true
1085   verbose = true
1086 #+end_src
1087 *** Tag
1088 #+begin_src conf :tangle ~/.gitconfig
1089   [tag]
1090   gpgsign = true
1091 #+end_src
1092 ** Zathura
1093 The best document reader!
1094 *** Options
1095 #+begin_src conf :tangle ~/.config/zathura/zathurarc
1096   map <C-i> recolor
1097   map <A-b> toggle_statusbar
1098   set selection-clipboard clipboard
1099   set scroll-step 200
1100
1101   set window-title-basename "true"
1102   set selection-clipboard "clipboard"
1103 #+end_src
1104 *** Colors
1105 #+begin_src conf :tangle ~/.config/zathura/zathurarc
1106   set default-bg         "#000000"
1107   set default-fg         "#ffffff"
1108   set render-loading     true
1109   set render-loading-bg  "#000000"
1110   set render-loading-fg  "#ffffff"
1111
1112   set recolor-lightcolor "#000000" # bg
1113   set recolor-darkcolor  "#ffffff" # fg
1114   set recolor            "true"
1115 #+end_src
1116 ** Xresources
1117 Modus operandi theme. No program I use checks for anything beyond foreground and background, but hey, it can't hurt to have all the colors in there.
1118 #+begin_src conf :tangle ~/.Xresources
1119   ! special
1120   ,*.foreground:   #ffffff
1121   ,*.background:   #000000
1122   ,*.cursorColor:  #ffffff
1123
1124   ! black
1125   ,*.color0:       #000000
1126   ,*.color8:       #555555
1127
1128   ! red
1129   ,*.color1:       #ff8059
1130   ,*.color9:       #ffa0a0
1131
1132   ! green
1133   ,*.color2:       #00fc50
1134   ,*.color10:      #88cf88
1135
1136   ! yellow
1137   ,*.color3:       #eecc00
1138   ,*.color11:      #d2b580
1139
1140   ! blue
1141   ,*.color4:       #29aeff
1142   ,*.color12:      #92baff
1143
1144   ! magenta
1145   ,*.color5:       #feacd0
1146   ,*.color13:      #e0b2d6
1147
1148   ! cyan
1149   ,*.color6:       #00d3d0
1150   ,*.color14:      #a0bfdf
1151
1152   ! white
1153   ,*.color7:       #eeeeee
1154   ,*.color15:      #dddddd
1155 #+end_src
1156 ** Tmux
1157 I use tmux in order to keep my st build light. Still learning how it works.
1158 #+begin_src conf :tangle ~/.tmux.conf
1159   set -g status off
1160   set -g mouse on
1161   set-option -g history-limit 50000
1162   set-window-option -g mode-keys vi
1163   bind-key -T copy-mode-vi 'v' send -X begin-selection
1164   bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel 'xclip -in -selection clipboard'
1165 #+end_src
1166 ** GPG
1167 *** Config
1168 #+begin_src conf :tangle ~/.gnupg/gpg.conf
1169   default-key 3C9ED82FFE788E4A
1170   use-agent
1171 #+end_src
1172 *** Agent
1173 #+begin_src conf :tangle ~/.gnupg/gpg-agent.conf
1174   pinentry-program /sbin/pinentry
1175   max-cache-ttl 600
1176   default-cache-ttl 600
1177   allow-emacs-pinentry
1178 #+end_src
1179 ** Xmodmap
1180 #+begin_src conf (if (eq system-name "frost.armaanb.net") ":tangle ~/.config/xmodmap")
1181   ! Unmap left super
1182   clear mod4
1183
1184   ! Turn right alt into super
1185   remove mod1 = Alt_R
1186   add mod4 = Alt_R
1187
1188   ! Swap caps and control
1189   remove Lock = Caps_Lock
1190   remove Control = Control_L
1191   remove Lock = Control_L
1192   remove Control = Caps_Lock
1193   keysym Control_L = Caps_Lock
1194   keysym Caps_Lock = Control_L
1195   add Lock = Caps_Lock
1196   add Control = Control_L
1197 #+end_src