aboutsummaryrefslogtreecommitdiff
path: root/Emacs/.config/emacs/core.org
blob: 9ff0905df81b9f94e96b09522e773730867f47f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
#+title: Core packages
#+author: Aryadev Chavali
#+description: The core components of my configuration
#+date: 2023-09-29
#+property: header-args:emacs-lisp :tangle core.el :comments link :results none
#+options: toc:nil
#+startup: noindent

Packages that are absolutely necessary for the rest of the
configuration.  These yield core functionality such as keybinding,
modal editing, completion, auto typing to name a few.
* General
General provides a great solution for binding keys.  It has evil and
use-package support so it fits nicely into configuration.  In this
case, I define a "definer" for the "LEADER" keys.  Leader is bound to
~SPC~ and it's functionally equivalent to the doom/spacemacs leader.
Local leader is bound to ~SPC ,~ and it's similar to doom/spacemacs
leader but doesn't try to fully assimilate the local-leader map,
instead just picking stuff I think is useful.  This forces me to learn
only as many bindings as I find necessary; no more, no less.

I also define prefix leaders for differing applications.  These are
quite self explanatory by their name and provide a nice way to
visualise all bindings under a specific heading just by searching the
code.
#+begin_src emacs-lisp
(use-package general
  :straight t
  :demand t
  :config
  ;; General which key definitions for leaders
  (general-def
    :states '(normal motion)
    "SPC"   'nil
    "\\"    '(nil :which-key "Local leader")
    "SPC a" '(nil :which-key "Applications")
    "SPC b" '(nil :which-key "Buffers")
    "SPC c" '(nil :which-key "Code")
    "SPC d" '(nil :which-key "Directories")
    "SPC f" '(nil :which-key "Files")
    "SPC i" '(nil :which-key "Insert")
    "SPC m" '(nil :which-key "Modes")
    "SPC q" '(nil :which-key "Quit/Literate")
    "SPC s" '(nil :which-key "Search")
    "SPC t" '(nil :which-key "Shell"))

  (general-create-definer leader
    :states '(normal motion)
    :keymaps 'override
    :prefix "SPC")

  (general-create-definer local-leader
    :states '(normal motion)
    :prefix "\\")

  (general-create-definer code-leader
    :states '(normal motion)
    :keymaps 'override
    :prefix "SPC c")

  (general-create-definer file-leader
    :states '(normal motion)
    :keymaps 'override
    :prefix "SPC f")

  (general-create-definer shell-leader
    :states '(normal motion)
    :keymaps 'override
    :prefix "SPC t")

  (general-create-definer mode-leader
    :states '(normal motion)
    :keymaps 'override
    :prefix "SPC m")

  (general-create-definer app-leader
    :states '(normal motion)
    :keymaps 'override
    :prefix "SPC a")

  (general-create-definer search-leader
    :states '(normal motion)
    :keymaps 'override
    :prefix "SPC s")

  (general-create-definer buffer-leader
    :states '(normal motion)
    :keymaps 'override
    :prefix "SPC b")

  (general-create-definer quit-leader
    :states '(normal motion)
    :keymaps 'override
    :prefix "SPC q")

  (general-create-definer insert-leader
    :states '(normal motion)
    :keymaps 'override
    :prefix "SPC i")

  (general-create-definer dir-leader
    :states '(normal motion)
    :keymaps 'override
    :prefix "SPC d")

  (general-create-definer general-nmmap
    :states '(normal motion))

  (defalias 'nmmap #'general-nmmap)

  (general-evil-setup t))
#+end_src
** Some binds in Emacs
Some bindings that I couldn't fit elsewhere easily.
#+begin_src emacs-lisp
(use-package emacs
  :straight nil
  :general
  (general-def
    "C-x d" #'delete-frame)

  (nmmap
    "C--" #'text-scale-decrease
    "C-=" #'text-scale-increase
    "C-+" #'text-scale-adjust)

  (leader
    "SPC" '(execute-extended-command :which-key "M-x")
    "'"   '(browse-url-emacs :which-key "Open url in Emacs")
    ";"   'eval-expression
    ":"   `(,(proc (interactive) (switch-to-buffer "*scratch*"))
            :which-key "Switch to *scratch*")
    "!"   '(async-shell-command :which-key "Async shell command")
    "h" '(help-command :which-key "Help"))

  (mode-leader
    "t" #'+oreo/switch-theme)

  (code-leader
    "F" `(,(proc (interactive) (find-file "~/Code/"))
          :which-key "Open ~/Code/"))

  (file-leader
    "f" #'find-file
    "F" #'find-file-other-frame
    "s" #'save-buffer
    "p" `(,(proc (interactive)
                 (find-file (concat user-emacs-directory "config.org")))
          :which-key "Open config.org"))

  (quit-leader
    "q" #'save-buffers-kill-terminal
    "c" #'+literate/compile-config
    "l" #'+literate/load-config
    "d" #'delete-frame)

  (search-leader "i" #'imenu))
#+end_src
* Evil
My editor journey started off with Vim rather than Emacs, so my brain
has imprinted on its style.  Thankfully Emacs is super extensible so
there exists a package (more of a supreme system) for porting Vim's
modal editing style to Emacs, called Evil (Emacs Vi Layer).

However there are a lot of packages in Vim that provide greater
functionality, for example 'vim-surround'.  Emacs, by default, has
these capabilities but there are further packages which integrate them
into Evil.
** Evil core
Setup the evil package, with some opinionated keybindings:
- Switch ~evil-upcase~ and ~evil-downcase~ because I use ~evil-upcase~
  more
- Switch ~evil-goto-mark~ and ~evil-goto-mark-line~ as I'd rather have
  the global one closer to the home row
- Use 'T' character as an action for transposing objects
#+begin_src emacs-lisp
(use-package evil
  :demand t
  :hook (after-init-hook . evil-mode)
  :general
  (leader
    "w"  '(evil-window-map :which-key "Window")
    "wd" #'delete-frame)

  (nmmap
    "TAB" #'evil-jump-item
    "r"   #'evil-replace-state
    "zC"  #'hs-hide-level
    "zO" #'hs-show-all
    "'"   #'evil-goto-mark
    "`"   #'evil-goto-mark-line
    "C-w" #'evil-window-map
    "gu"  #'evil-upcase
    "gU"  #'evil-downcase
    "T"   nil)

  (nmmap
    :infix "T"
    "w" #'transpose-words
    "c" #'transpose-chars
    "s" #'transpose-sentences
    "p" #'transpose-paragraphs
    "e" #'transpose-sexps
    "l" #'transpose-lines)
  :init
  (setq evil-want-keybinding nil
        evil-split-window-below t
        evil-vsplit-window-right t
        evil-want-abbrev-expand-on-insert-exit t
        evil-undo-system #'undo-tree)
  :config
  (fset #'evil-window-vsplit #'make-frame))
#+end_src
** Evil surround
Evil surround is a port for vim-surround.
#+begin_src emacs-lisp
(use-package evil-surround
  :after evil
  :config
  (global-evil-surround-mode))
#+end_src
** Evil commentary
Allows generalised commenting of objects easily.
#+begin_src emacs-lisp
(use-package evil-commentary
  :after evil
  :config
  (evil-commentary-mode))
#+end_src
** Evil multi cursor
Setup for multi cursors in Evil mode.  Don't let evil-mc setup it's own
keymap because it uses 'gr' as its prefix, which I don't like.
#+begin_src emacs-lisp
(use-package evil-mc
  :after evil
  :init
  (defvar evil-mc-key-map (make-sparse-keymap))
  :general
  (nmap
    :infix "gz"
    "q" #'evil-mc-undo-all-cursors
    "d" #'evil-mc-make-and-goto-next-match
    "j" #'evil-mc-make-cursor-move-next-line
    "k" #'evil-mc-make-cursor-move-prev-line
    "j" #'evil-mc-make-cursor-move-next-line
    "m" #'evil-mc-make-all-cursors
    "z" #'evil-mc-make-cursor-here
    "r" #'evil-mc-resume-cursors
    "s" #'evil-mc-pause-cursors
    "u" #'evil-mc-undo-last-added-cursor)
  :config
  ;; (evil-mc-define-vars)
  ;; (evil-mc-initialize-vars)
  ;; (add-hook 'evil-mc-before-cursors-created #'evil-mc-pause-incompatible-modes)
  ;; (add-hook 'evil-mc-before-cursors-created #'evil-mc-initialize-active-state)
  ;; (add-hook 'evil-mc-after-cursors-deleted  #'evil-mc-teardown-active-state)
  ;; (add-hook 'evil-mc-after-cursors-deleted  #'evil-mc-resume-incompatible-modes)
  ;; (advice-add #'evil-mc-initialize-hooks :override #'ignore)
  ;; (advice-add #'evil-mc-teardown-hooks :override #'evil-mc-initialize-vars)
  ;; (advice-add #'evil-mc-initialize-active-state :before #'turn-on-evil-mc-mode)
  ;; (advice-add #'evil-mc-teardown-active-state :after #'turn-off-evil-mc-mode)
  ;; (add-hook 'evil-insert-state-entry-hook #'evil-mc-resume-cursors)
  (global-evil-mc-mode))
#+end_src

** Evil collection
Provides a community based set of keybindings for most modes in
Emacs.  I don't necessarily like all my modes having these bindings
though, as I may disagree with some.  So I use it in a mode to mode basis.
#+begin_src emacs-lisp
(use-package evil-collection
  :after evil)
#+end_src
* Completion
Emacs is a text based interface.  Completion is its bread and butter
in providing good user experience.  By default Emacs provides
'completions-list' which produces a buffer of options which can be
searched and selected.  We can take this further though!

Ido and Icomplete are packages distributed with Emacs to provide
greater completion interfaces.  They utilise the minibuffer to create
a more interactive experience, allowing incremental searches and
option selection.

Ivy and Helm provide more modern interfaces, though Helm is quite
heavy.  Ivy, on the other hand, provides an interface similar to Ido
with less clutter and better customisation options.
** Ivy
Ivy is a completion framework for Emacs, and my preferred one.  It has
a great set of features with little to no pain with setting up.
*** Ivy Core
Setup for ivy, in preparation for counsel.  Turn on ivy-mode just
after init.

Setup vim-like bindings for the minibuffer ("M-(j|k)" for down|up the
selection list).
#+begin_src emacs-lisp
(use-package ivy
  :defer t
  :hook (after-init-hook . ivy-mode)
  :general
  (general-def
    :keymaps 'ivy-minibuffer-map
    "C-j"    #'ivy-yank-symbol
    "M-j"    #'ivy-next-line-or-history
    "M-k"    #'ivy-previous-line-or-history
    "C-SPC"  #'ivy-occur)
  (general-def
    :keymaps  'ivy-switch-buffer-map
    "M-j"     #'ivy-next-line-or-history
    "M-k"     #'ivy-previous-line-or-history)
  (nmap
    :keymaps '(ivy-occur-mode-map ivy-occur-grep-mode-map)
    "RET"    #'ivy-occur-press-and-switch
    "J"      #'ivy-occur-press
    "gr"     #'ivy-occur-revert-buffer
    "q"      #'quit-window
    "D"      #'ivy-occur-delete-candidate
    "W"      #'ivy-wgrep-change-to-wgrep-mode
    "{"      #'compilation-previous-file
    "}"      #'compilation-next-file)
  :init
  (with-eval-after-load "evil"
    (evil-set-initial-state 'ivy-occur-mode 'normal)
    (evil-set-initial-state 'ivy-occur-grep-mode 'normal))
  (with-eval-after-load "amx"
    (setq amx-backend 'ivy))

  (setq ivy-height 10
        ivy-wrap t
        ivy-fixed-height-minibuffer t
        ivy-use-virtual-buffers nil
        ivy-virtual-abbreviate 'full
        ivy-on-del-error-function #'ignore
        ivy-use-selectable-prompt t)
  :config
  (require 'counsel nil t))
#+end_src
*** Counsel
Setup for counsel.  Load after ivy and helpful.
#+begin_src emacs-lisp
(use-package counsel
  :defer t
  :general
  (search-leader
    "s" #'counsel-grep-or-swiper
    "r" #'counsel-rg)
  (file-leader
    "r" #'counsel-recentf)
  (insert-leader
    "c" #'counsel-unicode-char)
  (general-def
    [remap describe-bindings]        #'counsel-descbinds
    [remap load-theme]               #'counsel-load-theme)
  :config
  (setq ivy-initial-inputs-alist '((org-insert-link . "^"))
        counsel-describe-function-function #'helpful-callable
        counsel-describe-variable-function #'helpful-variable
        counsel-grep-swiper-limit 1500000
        ivy-re-builders-alist '((swiper . ivy--regex-plus)
                                (counsel-grep-or-swiper . ivy--regex-plus)
                                (counsel-rg . ivy--regex-plus)
                                (t . ivy--regex-ignore-order)))
  (counsel-mode))
#+end_src
*** WIP Ivy posframe
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
This makes ivy minibuffer windows use child frames.
Very nice eyecandy, but can get kinda annoying.
#+begin_src emacs-lisp
(use-package ivy-posframe
  :hook (ivy-mode-hook . ivy-posframe-mode)
  :straight t
  :init
  (setq ivy-posframe-parameters
        '((left-fringe      . 0)
          (right-fringe     . 0)
          (background-color . "grey7")))

  (setq ivy-posframe-display-functions-alist
        '((t . ivy-posframe-display-at-window-center))))
#+end_src
*** WIP Counsel etags
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
Counsel etags allows me to search generated tag files for tags.  I
already have a function defined to generate the tags, so it's just
searching them which I find to be a bit of a hassle, and where this
package comes in.

This has been replaced by [[*xref][xref]] which is inbuilt.
#+begin_src emacs-lisp
(use-package counsel-etags
  :after counsel
  :general
  (search-leader
    "t" #'counsel-etags-find-tag))
#+end_src
** WIP Ido
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
Ido is a very old completion package that still works great to this
day.  Though it is limited in its scope (and may thus be called a
completion add-on rather than a full on framework), it is still a very
powerful package.  With the use of ido-completing-read+, it may be used
similarly to a fully fledged completion framework.

#+begin_src emacs-lisp
(use-package ido
  :demand t
  :general
  (general-def
    :keymaps '(ido-buffer-completion-map
               ido-file-completion-map
               ido-file-dir-completion-map
               ido-common-completion-map)
    (kbd "M-j")   #'ido-next-match
    (kbd "M-k")   #'ido-prev-match
    (kbd "C-x o") #'evil-window-up)
  :init
  (setq ido-decorations
        (list "{" "}" " \n" " ..." "[" "]" " [No match]" " [Matched]"
              " [Not readable]" " [Too big]" " [Confirm]")
        completion-styles '(flex partial-completion intials emacs22))
  (setq-default ido-enable-flex-matching t
                ido-enable-dot-prefix t
                ido-enable-regexp nil)
  (with-eval-after-load "magit"
    (setq magit-completing-read-function 'magit-ido-completing-read))
  :config
  (ido-mode)
  (ido-everywhere))
#+end_src
*** Ido ubiquitous
Ido completing-read+ is a package that extends the ido package to work
with more text based functions.
#+begin_src emacs-lisp
(use-package ido-completing-read+
  :after ido
  :config
  (ido-ubiquitous-mode +1))
#+end_src
** Amx
Amx is a fork of Smex that works to enhance the
execute-extended-command interface.  It also provides support for ido
or ivy (though I'm likely to use ido here) and allows you to switch
between them.

It provides a lot of niceties such as presenting the key bind when
looking for a command.

#+begin_src emacs-lisp
(use-package amx
  :config
  (amx-mode))
#+end_src
** Orderless
Orderless sorting method for completion, probably one of the best
things ever.
#+begin_src emacs-lisp
(use-package orderless
  :after (ivy ido)
  :config
  (setf (alist-get t ivy-re-builders-alist) 'orderless-ivy-re-builder))
#+end_src
** Completions-list
In case I ever use the completions list, some basic commands to look
around.
#+begin_src emacs-lisp
(use-package simple
  :straight nil
  :general
  (nmmap
    :keymaps 'completion-list-mode-map
    "l"   #'next-completion
    "h"   #'previous-completion
    "ESC" #'delete-completion-window
    "q"   #'quit-window
    "RET" #'choose-completion)
  :config
  (with-eval-after-load "evil"
    (setq evil-emacs-state-modes (cl-remove-if
                                  #'(lambda (x) (eq 'completions-list-mode x))
                                  evil-emacs-state-modes))
    (add-to-list 'evil-normal-state-modes 'completions-list-mode)))
#+end_src
** Company
Company is the auto complete system I use.  I don't like having heavy
setups for company as it only makes it slower to use.  In this case,
just setup some evil binds for company.
#+begin_src emacs-lisp
(use-package company
  :straight t
  :hook
  (prog-mode-hook   . company-mode)
  (eshell-mode-hook . company-mode)
  :general
  (imap
    "C-SPC" #'company-complete)
  (general-def
    :states '(normal insert)
    "M-j" #'company-select-next
    "M-k" #'company-select-previous))
#+end_src
* Pretty symbols
Prettify symbols mode allows for users to declare 'symbols' that
replace text within certain modes.  Though this may seem like useless
eye candy, it has aided my comprehension and speed of recognition
(recognising symbols is easier than words).

Essentially a use-package keyword which makes declaring pretty symbols
for language modes incredibly easy.  Checkout my [[C/C++][C/C++]] configuration
for an example.
#+begin_src emacs-lisp
(use-package prog-mode
  :straight nil
  :init
  (setq prettify-symbols-unprettify-at-point t)
  :config
  (with-eval-after-load "use-package-core"
    (add-to-list 'use-package-keywords ':pretty)
    (defun use-package-normalize/:pretty (_name-symbol _keyword args)
      args)

    (defun use-package-handler/:pretty (name _keyword args rest state)
      (use-package-concat
       (use-package-process-keywords name rest state)
       (mapcar
        #'(lambda (arg)
            (let ((mode (car arg))
                  (rest (cdr arg)))
              `(add-hook
                ',mode
                #'(lambda nil
                    (setq prettify-symbols-alist ',rest)
                    (prettify-symbols-mode)))))
        args)))))
#+end_src

Here's a collection of keywords and possible associated symbols for
any prog language of choice.  Mostly for reference and copying.
#+begin_example
("null"   . "Ø")
("list"   . "ℓ")
("string" . "𝕊")
("true"   . "⊤")
("false"  . "⊥")
("char"   . "ℂ")
("int"    . "ℤ")
("float"  . "ℝ")
("!"      . "¬")
("&&"     . "∧")
("||"      . "∨")
("for"    . "∀")
("return" . "⟼")
("print"  . "ℙ")
("lambda" . "λ")
#+end_example
* Window management
Emacs' default window management is quite bad, eating other windows on
a whim and not particularly caring for the current window setup.
Thankfully you can change this via the ~display-buffer-alist~ which
matches buffer names with how the window for the buffer should be
displayed.  I add a use-package keyword to make ~display-buffer-alist~
records within use-package.

I have no idea whether it's optimal AT ALL, but it works for me.
#+begin_src emacs-lisp
(use-package window
  :straight nil
  :general
  (buffer-leader
    "b" #'switch-to-buffer
    "d" #'kill-current-buffer
    "K" #'kill-buffer
    "j" #'next-buffer
    "k" #'previous-buffer
    "D" '(+oreo/clean-buffer-list :which-key "Kill most buffers"))
  :init
  (with-eval-after-load "use-package-core"
    (add-to-list 'use-package-keywords ':display)
    (defun use-package-normalize/:display (_name-symbol _keyword args)
      args)

    (defun use-package-handler/:display (name _keyword args rest state)
      (use-package-concat
       (use-package-process-keywords name rest state)
       (mapcar
        #'(lambda (arg)
            `(add-to-list 'display-buffer-alist
                          ',arg))
        args)))))
#+end_src
** Some display records
Using the ~:display~ keyword, setup up some ~display-buffer-alist~
records.  This is mostly for packages that aren't really configured
(like [[info:woman][woman]]) or packages that were configured before
(like [[Ivy][Ivy]]).
#+begin_src emacs-lisp
(use-package window
  :straight nil
  :defer t
  :display
  ("\\*Process List\\*"
   (display-buffer-at-bottom)
   (window-height . 0.25))

  ("\\*\\(Ido \\)?Completions\\*"
   (display-buffer-in-side-window)
   (window-height . 0.25)
   (side . bottom))

  ("\\*ivy-occur.*"
   (display-buffer-at-bottom)
   (window-height . 0.25))

  ("\\*Async Shell Command\\*"
   (display-buffer-at-bottom)
   (window-height . 0.25)))
#+end_src
* Auto typing
Snippets are a pretty nice way of automatically inserting code.  Emacs
provides a ton of packages by default to do this, but there are great
packages to install as well.

Abbrevs and skeletons make up a popular solution within Emacs default.
Abbrevs are for simple expressions wherein the only input is the key,
and the output is some Elisp function.  They provide a lot of inbuilt
functionality and are quite useful.  Skeletons, on the other hand, are
for higher level insertions

The popular external solution is Yasnippet.  Yasnippet is a great
package for snippets, which I use heavily in programming and org-mode.
I setup here the global mode for yasnippet and a collection of
snippets for ease of use.
** Abbrevs
Just define a few abbrevs for various date-time operations.  Also
define a macro that will assume a function for the expansion, helping
with abstracting a few things away.
#+begin_src emacs-lisp
(use-package abbrev
  :straight nil
  :hook
  (prog-mode-hook . abbrev-mode)
  (text-mode-hook . abbrev-mode)
  :init
  (defmacro +abbrev/define-abbrevs (abbrev-table &rest abbrevs)
    `(progn
       ,@(mapcar #'(lambda (abbrev)
                     `(define-abbrev
                        ,abbrev-table
                        ,(car abbrev)
                        ""
                        (proc (insert ,(cadr abbrev)))))
                 abbrevs)))
  (setq save-abbrevs nil)
  :config
  (+abbrev/define-abbrevs
   global-abbrev-table
   ("sdate"
    (format-time-string "%Y-%m-%d" (current-time)))
   ("stime"
    (format-time-string "%H:%M:%S" (current-time)))
   ("sday"
    (format-time-string "%A" (current-time)))
   ("smon"
    (format-time-string "%B" (current-time)))))
#+end_src
** WIP Skeletons
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
Defines a macro for generating a skeleton + abbrev for a given mode.
Doesn't sanitise inputs because I assume callers are /rational/ actors
who would *only* use this for their top level Emacs config.

Honestly didn't find much use for this currently, so disabled.
#+begin_src emacs-lisp
(use-package skeleton
  :straight nil
  :after abbrev
  :config
  (defmacro +autotyping/gen-skeleton-abbrev (mode abbrev &rest skeleton)
    (let* ((table          (intern (concat (symbol-name mode) "-abbrev-table")))
           (skeleton-name  (intern (concat "+skeleton/" (symbol-name mode) "/" abbrev))))
      `(progn
         (define-skeleton
           ,skeleton-name
           ""
           ,@skeleton)
         (define-abbrev ,table
           ,abbrev
           ""
           ',skeleton-name)))))
#+end_src
** Auto insert
Allows inserting text immediately upon creating a new buffer with a
given name.  Supports skeletons for inserting text.  To make it easier
for later systems to define their own auto inserts, I define a
~use-package~ keyword ~auto-insert~ which allows one to define an
entry for ~auto-insert-alist~.
#+begin_src emacs-lisp
(use-package autoinsert
  :straight nil
  :demand t
  :hook (after-init-hook . auto-insert-mode)
  :config
  (with-eval-after-load "use-package-core"
    (add-to-list 'use-package-keywords ':auto-insert)
    (defun use-package-normalize/:auto-insert (_name-symbol _keyword args)
      args)
    (defun use-package-handler/:auto-insert (name _keyword args rest state)
      (use-package-concat
       (use-package-process-keywords name rest state)
       (mapcar
        #'(lambda (arg)
            `(add-to-list
              'auto-insert-alist
              ',arg))
        args)))))
#+end_src
** Yasnippet default
Look at the snippets [[file:../.config/yasnippet/snippets/][folder]]
for all snippets I've got.
#+begin_src emacs-lisp
(use-package yasnippet
  :defer t
  :hook
  (prog-mode-hook . yas-minor-mode)
  :general
  (insert-leader
    "i" #'yas-insert-snippet)
  :config
  (yas-load-directory (no-littering-expand-etc-file-name "yasnippet/snippets")))
#+end_src