aboutsummaryrefslogtreecommitdiff
path: root/Emacs/.config/emacs/app.org
blob: d4ac94d8d76186cbeff92c62d1c3f9e55014df3a (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
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
#+title: Applications for Emacs
#+author: Aryadev Chavali
#+description: Applications for my Emacs OS™
#+date: 2023-09-29
#+property: header-args:emacs-lisp :tangle app.el :comments link :results none
#+options: toc:nil
#+startup: noindent

Applications are greater than packages; they provide a set of
functionality to create an interface in Emacs.  Emacs comes with
applications and others may be installed.

* WAIT Dashboard
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
Dashboard creates a custom dashboard for Emacs that replaces the
initial startup screen in default Emacs.  It has a lot of customising
options.

Unfortunately not that useful, many things are easier to invoke
directly such as recent files or project changing.
#+begin_src emacs-lisp
(use-package dashboard
  :straight t
  :demand t
  :general
  (app-leader
    "b" #'dashboard-refresh-buffer)
  (:states '(normal motion emacs)
   :keymaps 'dashboard-mode-map
   "q" (proc (interactive) (kill-this-buffer)))
  (nmmap
    :keymaps 'dashboard-mode-map
    "r" #'dashboard-jump-to-recent-files
    "p" #'dashboard-jump-to-projects
    "}" #'dashboard-next-section
    "{" #'dashboard-previous-section)
  :init
  (setq initial-buffer-choice nil
        dashboard-banner-logo-title "Oreomacs"
        dashboard-center-content t
        dashboard-set-init-info t
        dashboard-startup-banner (no-littering-expand-etc-file-name "dashboard/logo.png")
        dashboard-set-footer t
        dashboard-set-navigator t
        dashboard-items '((projects . 5)
                          (recents . 5))
        dashboard-footer-messages (list
                                   "Collecting parentheses..."
                                   "Linking 'coffee_machine.o'..."
                                   "Uploading ip to hacker named 4chan..."
                                   "Dividing by zero..."
                                   "Solving 3-sat..."
                                   "Obtaining your health record..."
                                   (format "Recompiling Emacs for the %dth time..." (random 1000))
                                   "Escaping the cycle of samsara..."))
  :config
  (dashboard-setup-startup-hook))
#+end_src
* EWW
Emacs Web Wowser is the inbuilt text based web browser for Emacs.  It
can render images and basic CSS styles but doesn't have a JavaScript
engine, which makes sense as it's primarily a text interface.
#+begin_src emacs-lisp
(use-package eww
  :defer t
  :general
  (app-leader
    "w" #'eww)
  (nmmap
    :keymaps 'eww-mode-map
    "w" #'evil-forward-word-begin
    "Y" #'shr-probe-and-copy-url)
  :straight nil
  :config
  (with-eval-after-load "evil-collection"
    (evil-collection-eww-setup)))
#+end_src
* Calendar
Calendar is a simple inbuilt application that helps with date
functionalities.  I add functionality to copy dates from the calendar
to the kill ring and bind it to "Y".
#+begin_src emacs-lisp
(use-package calendar
  :straight nil
  :defer t
  :commands (+calendar/copy-date +calendar/toggle-calendar)
  :display
  ("\\*Calendar\\*"
   (display-buffer-at-bottom)
   (inhibit-duplicate-buffer . t)
   (window-height . 0.17))
  :general
  (nmmap
    :keymaps 'calendar-mode-map
    "Y" #'+calendar/copy-date)
  (app-leader
    "d" #'calendar)
  :config
  (defun +calendar/copy-date ()
    "Copy date under cursor into kill ring."
    (interactive)
    (if (use-region-p)
        (call-interactively #'kill-ring-save)
      (let ((date (calendar-cursor-to-date)))
        (when date
          (setq date (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))
          (kill-new (format-time-string "%Y-%m-%d" date)))))))
#+end_src
* Mail
Mail is a funny thing; most people use it just for business or
advertising and it's come out of use in terms of personal
communication in the west for the most part (largely due to "social"
media applications).  However, this isn't true for the open source and
free software movement who heavily use mail for communication.

Integrating mail into Emacs helps as I can send source code and
integrate it into my workflow just a bit better.
** Notmuch
#+begin_src emacs-lisp
(use-package notmuch
  :defer t
  :commands (notmuch +mail/flag-thread)
  :general
  (app-leader "m" #'notmuch)
  (nmap
    :keymaps 'notmuch-search-mode-map
    "f" #'+mail/flag-thread)
  :init
  (defconst +mail/signature "---------------\nAryadev Chavali")
  (defconst +mail/local-dir (no-littering-expand-var-file-name "mail/"))
  (setq notmuch-show-logo nil
        notmuch-search-oldest-first nil
        notmuch-hello-sections '(notmuch-hello-insert-saved-searches
                                 notmuch-hello-insert-alltags
                                 notmuch-hello-insert-recent-searches)
        notmuch-archive-tags '("-inbox" "-unread" "+archive")
        mail-signature +mail/signature
        mail-default-directory +mail/local-dir
        mail-source-directory +mail/local-dir
        message-signature +mail/signature
        message-auto-save-directory +mail/local-dir
        message-directory +mail/local-dir)

  (defun +mail/sync-mail ()
    "Sync mail via mbsync."
    (interactive)
    (start-process-shell-command "" nil "mbsync -a"))
  (defun +mail/trash-junk ()
    "Delete any mail in junk"
    (interactive)
    (start-process-shell-command "" nil "notmuch search --output=files --format=text0 tag:deleted tag:spam tag:trash tag:junk | xargs -r0 rm"))
  :config
  (defun +mail/flag-thread (&optional unflag beg end)
    (interactive (cons current-prefix-arg (notmuch-interactive-region)))
    (notmuch-search-tag
     (notmuch-tag-change-list '("-inbox" "+flagged") unflag) beg end)
    (when (eq beg end)
      (notmuch-search-next-thread)))

  (advice-add #'notmuch-poll-and-refresh-this-buffer :after
              #'+mail/trash-junk)
  (with-eval-after-load "evil-collection"
    (evil-collection-notmuch-setup)))
#+end_src
** Smtpmail
#+begin_src emacs-lisp
(use-package smtpmail
  :straight nil
  :commands mail-send
  :init
  (setq-default
   smtpmail-smtp-server "mail.aryadevchavali.com"
   smtpmail-smtp-user "aryadev"
   smtpmail-smtp-service 587
   smtpmail-stream-type 'starttls
   send-mail-function #'smtpmail-send-it
   message-send-mail-function #'smtpmail-send-it))
#+end_src
* Dired
Setup for dired.  Make dired-hide-details-mode the default mode when
using dired-mode, as it removes the clutter.  Setup evil collection
for dired (even though dired doesn't really conflict with evil, there
are some corners I'd like to adjust).
#+begin_src emacs-lisp
(use-package dired
  :straight nil
  :commands (dired find-dired)
  :hook
  (dired-mode-hook              . auto-revert-mode)
  (dired-mode-hook              . dired-hide-details-mode)
  :init
  (setq-default dired-listing-switches "-AFBlu --group-directories-first"
                dired-omit-files "^\\."
                dired-dwim-target t
                image-dired-external-viewer "nsxiv")
  (with-eval-after-load "evil-collection"
    (evil-collection-dired-setup))
  :general
  (nmmap
    :keymaps 'dired-mode-map
    "SPC"   nil
    "SPC ," nil
    "T"     #'dired-create-empty-file
    "H"     #'dired-up-directory
    "L"     #'dired-find-file)
  (dir-leader
    "f" #'find-dired
    "d" #'dired
    "D" #'dired-other-window
    "i" #'image-dired
    "p" `(,(proc (interactive)
                 (dired "~/Text/PDFs/"))
          :which-key "Open PDFs"))
  (local-leader
    :keymaps 'dired-mode-map
    "i" #'dired-maybe-insert-subdir
    "I" #'+dired/insert-all-subdirectories
    "k" #'dired-prev-subdir
    "j" #'dired-next-subdir
    "K" #'dired-kill-subdir
    "m" #'dired-mark-files-regexp
    "u" #'dired-undo)
  (nmmap
    :keymaps 'image-dired-thumbnail-mode-map
    "h" #'image-dired-backward-image
    "l" #'image-dired-forward-image
    "j" #'image-dired-next-line
    "k" #'image-dired-previous-line
    "H" #'image-dired-display-previous
    "L" #'image-dired-display-next
    "RET" #'image-dired-display-this
    "m" #'image-dired-mark-thumb-original-file
    "q" #'quit-window)
  :config
  (add-to-list 'dired-guess-shell-alist-user '("\\.pdf\\'" "zathura"))
  (defun +dired/insert-all-subdirectories ()
    "Insert all subdirectories currently viewable."
    (interactive)
    (dired-mark-directories nil)
    (mapc #'dired-insert-subdir (dired-get-marked-files))
    (dired-unmark-all-marks)))
#+end_src
** fd-dired
Uses fd for finding file results in a directory: ~find-dired~ ->
~fd-dired~.

#+begin_src emacs-lisp
(use-package fd-dired
  :after dired
  :straight t
  :general
  (dir-leader
    "g" #'fd-dired))
#+end_src
** wdired
Similar to [[file:config.org::*(Rip)grep][wgrep]] =wdired= provides
the ability to use Emacs motions and editing on file names.  This
makes stuff like mass renaming and other file management tasks way
easier than even using the mark based system.
#+begin_src emacs-lisp
(use-package wdired
  :after dired
  :straight t
  :general
  (nmmap
    :keymaps 'dired-mode-map
    "W" #'wdired-change-to-wdired-mode)
  (nmmap
    :keymaps 'wdired-mode-map
    "ZZ" #'wdired-finish-edit
    "ZQ" #'wdired-abort-changes))
#+end_src
* WAIT Xwidget
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
Xwidget is a package which allows for the insertion of arbitrary
xwidgets into Emacs through buffers.  It must be compiled into Emacs
so you might need to customise your install.  One of its premier uses
is in navigating the web which it provides through the function
~xwidget-webkit-browse-url~.  This renders a fully functional web
browser within Emacs.

Though I am not to keen on using Emacs to browse the web /via/ xwidget
(EWW does a good job on its own), I am very interested in its
capability to render pages with JavaScript, as it may come of use when
doing web development.  I can see the results of work very quickly
without switching windows all within Emacs.

2023-10-20: Disabled as it didn't seem to work, and honestly wasn't
that useful.
** Xwidget Core
#+begin_src emacs-lisp
(use-package xwidget
  :straight nil
  :general
  (app-leader
    "u" #'xwidget-webkit-browse-url)
  (nmmap
    :keymaps 'xwidget-webkit-mode-map
    "q"         #'quit-window
    "h"         #'xwidget-webkit-scroll-backward
    "j"         #'xwidget-webkit-scroll-up
    "k"         #'xwidget-webkit-scroll-down
    "l"         #'xwidget-webkit-scroll-forward
    "+"         #'xwidget-webkit-zoom-in
    "-"         #'xwidget-webkit-zoom-out
    (kbd "C-f") #'xwidget-webkit-scroll-up
    (kbd "C-b") #'xwidget-webkit-scroll-down
    "H"         #'xwidget-webkit-back
    "L"         #'xwidget-webkit-forward
    "gu"        #'xwidget-webkit-browse-url
    "gr"        #'xwidget-webkit-reload
    "gg"        #'xwidget-webkit-scroll-top
    "G"         #'xwidget-webkit-scroll-bottom))
#+end_src
** Xwidget Extensions
Define a function ~+xwidget/render-file~ that reads a file name and
presents it in an xwidget.  If the current file is an HTML file, ask
if user wants to open current file.  Bind it to ~aU~ in the leader.

Also define a function ~+xwidget/search-query~ that first asks the
user what search engine they want to use ([[https://duckduckgo.com][Duck Duck Go]] and [[https://devdocs.io][DevDocs]]
currently) then asks for a query, which it parses then presents in an
xwidget window.  Bind to ~as~ in the leader.
#+begin_src emacs-lisp
(use-package xwidget
  :straight nil
  :commands (+xwidget/render-file +xwidget/search)
  :general
  (app-leader
    "U" #'+xwidget/render-file
    "s" #'+xwidget/search)
  :config
  (setenv "WEBKIT_FORCE_SANDBOX" "0")
  (defun +xwidget/render-file (&optional FORCE)
    "Find file (or use current file) and render in xwidget."
    (interactive)
    (cond
     ((and (not FORCE) (or (string= (replace-regexp-in-string ".*.html"
                                                              "html" (buffer-name)) "html")
                           (eq major-mode 'web-mode)
                           (eq major-mode 'html-mode))) ; If in html file
      (if (y-or-n-p "Open current file?: ") ; Maybe they want to open a separate file
          (xwidget-webkit-browse-url (format "file://%s" (buffer-file-name)))
        (+xwidget/render-file t))) ; recurse and open file via prompt
     (t
      (xwidget-webkit-browse-url
       (format "file://%s" (read-file-name "Enter file to open: "))))))

  (defun +xwidget/search ()
    "Run a search query on some search engine and display in
xwidget."
    (interactive)
    (let* ((engine (completing-read "Engine: " '("duckduckgo.com" "devdocs.io") nil t))
           (query-raw (read-string "Enter query: "))
           (query
            (cond
             ((string= engine "duckduckgo.com") query-raw)
             ((string= engine "devdocs.io") (concat "_ " query-raw)))))
      (xwidget-webkit-browse-url (concat "https://" engine "/?q=" query)))))
#+end_src
* Eshell
** Why Eshell?
Eshell is an integrated shell environment for Emacs, written in Emacs
Lisp.  I argue that it is the best shell/command interpreter to use in
Emacs.

Eshell is unlike the alternatives in Emacs as it's a /shell/ first,
not a terminal emulator. It has the ability to spoof some aspects of a
terminal emulator (through the shell parser), but it is NOT a terminal
emulator.

The killer benefits of eshell (which would appeal to Emacs users) are
a direct result of eshell being written in Emacs lisp:
- incredible integration with Emacs utilities (such as ~dired~,
  ~find-file~, any read functions, etc)
- very extensible, easy to write new commands which leverage Emacs
  commands as well as external utilities
- agnostic of platform: "eshell/cd" will call the underlying change
  directory function for you, so commands will (usually) mean the same
  thing regardless of platform
  - this means as long as Emacs can run on an operating system, one
    may run eshell

However, my favourite feature of eshell is the set of evaluators that
run on command input.  Some of the benefits listed above come as a
result of this powerful feature. These evaluators are described below.

Lisp evaluator: works on braced expressions, evaluating them as Lisp
expressions (e.g. ~(message "Hello, World!\n")~).  Any returned
objects are printed.  This makes eshell a LISP REPL!

External evaluator: works within curly braces, evaluating them via
some external shell process (like sh) (e.g. ~{echo "Hello,
world!\n"}~).  This makes eshell a (kinda dumb) terminal emulator!

The alias evaluator is the top level evaluator.  It is the main
evaluator for each expression given to eshell.  When given an
expression it tries to evaluate it by testing against these conditions:
- it's an alias defined by the user or in the ~eshell/~ namespace of
  functions (simplest evaluator)
- it's some form of lisp expression (lisp evaluator)
- it's an external command (bash evaluator)
Essentially, you get the best of both Emacs and external shell
programs *ALL WITHIN* Emacs for free.
** Eshell functionality
Bind some evil-like movements for easy shell usage, and a toggle
function to pull up the eshell quickly.
#+begin_src emacs-lisp
(use-package eshell
  :defer t
  :general
  (shell-leader
    "t" #'eshell)
  :init
  (add-hook
   'eshell-mode-hook
   (proc
    (interactive)
    (general-def
      :states '(normal insert)
      :keymaps 'eshell-mode-map
      "M-j" #'eshell-next-matching-input-from-input
      "M-k" #'eshell-previous-matching-input-from-input)
    (local-leader
      :keymaps 'eshell-mode-map
      "c" (proc (interactive) (eshell/clear)
                (recenter))
      "k" #'eshell-kill-process))))
#+end_src
** Eshell pretty symbols and display
Pretty symbols and a display record.
#+begin_src emacs-lisp
(use-package eshell
  :defer t
  :pretty
  (eshell-mode-hook
   ("lambda"  . "λ")
   ("numberp" . "ℤ")
   ("t"       . "⊨")
   ("nil"     . "Ø"))
  :display
  ("\\*e?shell\\*" ; for general shells as well
   (display-buffer-at-bottom)
   (window-height . 0.33)))
#+end_src
** Eshell variables and aliases
Set some sane defaults, a banner and a prompt.  The prompt checks for
a git repo in the current directory and provides some extra
information in that case (in particular, branch name and if there any
changes that haven't been committed).

#+begin_src emacs-lisp
(use-package eshell
  :defer t
  :config
  (defun +eshell/--git-get-remote-status ()
    (let* ((branch-status (split-string
                           (shell-command-to-string "git status | grep 'Your branch is'")))
           (status (nth 3 branch-status))
           (diff (cl-position "by" branch-status :test #'string=)))
      (if (null diff)
          (propertize "=" 'font-lock-face '(:foreground "green"))
        (let ((n (nth (+ 1 diff) branch-status)))
          (concat
           (cond
            ((string= status "ahead")
             (propertize "→ " 'font-lock-face '(:foreground "dodger blue")))
            ((string= status "behind")
             (propertize "← " 'font-lock-face '(:foreground "orange red"))))
           n)))))

  (defun +eshell/--git-get-change-status ()
    (let ((changed-files (- (length (split-string (shell-command-to-string "git status -s" ) "\n")) 1)))
      (if (= changed-files 0)
          (propertize "✓" 'font-lock-face '(:foreground "green"))
        (propertize (number-to-string changed-files) 'font-lock-face '(:foreground "red")))))

  (defun +eshell/get-git-properties ()
    (let ((git-branch (shell-command-to-string "git branch")))
      (if (or (string= git-branch "")
             (not (string= "*" (substring git-branch 0 1))))
          ""
        (format
         "(%s<%s>[%s])"
         (nth 2 (split-string git-branch "\n\\|\\*\\| "))
         (+eshell/--git-get-change-status)
         (+eshell/--git-get-remote-status)))))

  (defun +eshell/prompt-function ()
    (let ((git (+eshell/get-git-properties)))
      (mapconcat
       (lambda (item)
         (if (listp item)
             (propertize (car item)
                         'read-only t
                         'font-lock-face (cdr item)
                         'front-sticky   '(font-lock-face read-only)
                         'rear-nonsticky '(font-lock-face read-only))
           item))
       (list
        '("[")
        `(,(abbreviate-file-name (eshell/pwd)) :foreground "LimeGreen")
        '("]")
        (if (string= git "")
            ""
          (concat "-" git ""))
        "\n"
        `(,(format-time-string "[%H:%M:%S]") :foreground "purple")
        "\n"
        '("𝜆> " :foreground "DeepSkyBlue")))))

  (defun +eshell/banner-message ()
    (concat (shell-command-to-string "~/.local/scripts/cowfortune")
            "\n"))

  (setq eshell-cmpl-ignore-case t
        eshell-cd-on-directory t
        eshell-banner-message '(+eshell/banner-message)
        eshell-highlight-prompt nil
        eshell-prompt-function #'+eshell/prompt-function
        eshell-prompt-regexp "^𝜆> "))
#+end_src
** Eshell change directory quickly
Add ~eshell/goto~, which is actually a command accessible from within
eshell (this is because ~eshell/*~ creates an accessible function
within eshell with name ~*~).  ~eshell/goto~ makes it easier to change
directories by using Emacs' find-file interface (which is much faster
than ~cd ..; ls -l~).

~eshell/goto~ is a better ~cd~ for eshell.  However it is really just
a plaster over a bigger issue for my workflow; many times I want
eshell to be present in the current directory of the buffer I am
using.  So here's also a command for opening eshell with the current
directory.
#+begin_src emacs-lisp
(use-package eshell
  :defer t
  :general
  (leader
    "T" #'+eshell/current-buffer)
  :config
  (defun eshell/goto (&rest args)
    "Use `read-directory-name' to change directories."
    (eshell/cd (list (read-directory-name "Directory?: "))))

  (with-eval-after-load "projectile"
    (defun eshell/goto-project-root (&rest args)
      "Change to directory `projectile-project-root'"
      (if (projectile-project-root)
          (eshell/cd (list (projectile-project-root)))
        (eshell/echo "Projectile not active here..."))))

  (defun +eshell/current-buffer ()
    (interactive)
    (let  ((dir (if buffer-file-name
                    (file-name-directory buffer-file-name)
                  default-directory))
           (buf (eshell)))
      (if dir
          (with-current-buffer buf
            (eshell/cd dir)
            (eshell-send-input))
        (message "Could not switch eshell: buffer is not real file")))))
#+end_src
* WAIT Elfeed
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
Elfeed is the perfect RSS feed reader, integrated into Emacs
perfectly.  I've got a set of feeds that I use for a large variety of
stuff, mostly media and entertainment.  I've also bound "<leader> ar"
to elfeed for loading the system.
#+begin_src emacs-lisp
(use-package elfeed
  :general
  (app-leader "r" #'elfeed)
  (nmmap
    :keymaps 'elfeed-search-mode-map
    "gr"       #'elfeed-update
    "s"        #'elfeed-search-live-filter
    "<return>" #'elfeed-search-show-entry)
  :init
  (setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/"))

  (setq +rss/feed-urls
        '(("Arch Linux"
           "https://www.archlinux.org/feeds/news/"
           News Technology)
          ("The Onion"
           "https://www.theonion.com/rss"
           Social)
          ("Protesilaos Stavrou"
           "https://www.youtube.com/@protesilaos"
           YouTube Technology)
          ("Tsoding Daily"
           "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg"
           YouTube Technology)
          ("Tsoding"
           "https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg"
           YouTube Technology)
          ("Nexpo"
           "https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw"
           YouTube Stories)
          ("3B1B"
           "https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw"
           YouTube)
          ("Fredrik Knusden"
           "https://www.youtube.com/feeds/videos.xml?channel_id=UCbWcXB0PoqOsAvAdfzWMf0w"
           YouTube Stories)
          ("Barely Sociable"
           "https://www.youtube.com/feeds/videos.xml?channel_id=UC9PIn6-XuRKZ5HmYeu46AIw"
           YouTube Stories)
          ("Atrocity Guide"
           "https://www.youtube.com/feeds/videos.xml?channel_id=UCn8OYopT9e8tng-CGEWzfmw"
           YouTube Stories)
          ("Hacker News"
           "https://news.ycombinator.com/rss"
           Social News Technology)
          ("Hacker Factor"
           "https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2"
           Social)))
  :config
  (with-eval-after-load "evil-collection"
    (evil-collection-elfeed-setup))

  (setq elfeed-feeds (cl-map 'list #'(lambda (item)
                                       (append (list (nth 1 item)) (cdr (cdr item))))
                             +rss/feed-urls))

  (advice-add  'elfeed-search-show-entry :after #'+elfeed/dispatch-entry)

  (defun +elfeed/dispatch-entry (entry)
    "Process each type of entry differently.
  e.g., you may want to open HN entries in eww."
    (let ((url (elfeed-entry-link entry)))
      (pcase url
        ((pred (string-match-p "https\\:\\/\\/www.youtube.com\\/watch"))
         (mpv-play-url url))
        (_ (eww url))))))
#+end_src
* Magit
Magit is *the* git porcelain for Emacs, which perfectly encapsulates
the git cli.  In this case I just need to setup the bindings for it.
As magit will definitely load after evil (as it must be run by a
binding, and evil will load after init), I can use evil-collection
freely.  Also, define an auto insert for commit messages so that I
don't need to write everything myself.

#+begin_src emacs-lisp
(use-package magit
  :defer t
  :display
  ("magit:.*"
   (display-buffer-same-window)
   (inhibit-duplicate-buffer . t))
  ("magit-diff:.*"
   (display-buffer-below-selected))
  ("magit-log:.*"
   (display-buffer-same-window))
  :general
  (leader
    "g" '(magit-dispatch :which-key "Magit"))
  (code-leader
    "b" #'magit-blame)
  :auto-insert
  (("COMMIT_EDITMSG" . "Commit skeleton")
   ""
   "(" (read-string "Enter feature/module: ") ")"
   (read-string "Enter simple description: ") "\n\n")
  :init
  (setq vc-follow-symlinks t
        magit-blame-echo-style 'lines
        magit-copy-revision-abbreviated t)
  :config
  (with-eval-after-load "evil"
    (evil-set-initial-state 'magit-status-mode 'motion))
  (with-eval-after-load "evil-collection"
    (evil-collection-magit-setup)))
#+end_src
* IBuffer
IBuffer is the dired of buffers: providing the ability to mark
buffers, mass rename/delete and just observe stuff.
#+begin_src emacs-lisp
(use-package ibuffer
  :general
  (buffer-leader
    "i" #'ibuffer)
  :config
  (with-eval-after-load "evil-collection"
    (evil-collection-ibuffer-setup)))
#+end_src
* Proced
Emacs has two systems for process management:
+ proced: a general 'top' like interface which allows general
  management of linux processes
+ list-processes: a specific Emacs based system that lists processes
  spawned by Emacs (similar to a top for Emacs specifically)

Core proced config, just a few bindings and evil collection setup.
#+begin_src emacs-lisp
(use-package proced
  :straight nil
  :general
  (app-leader
    "p" #'proced)
  (nmap
    :keymaps 'proced-mode-map
    "za" #'proced-toggle-auto-update)
  :display
  ("\\*Proced\\*"
   (display-buffer-at-bottom)
   (window-height . 0.25))
  :init
  (setq proced-auto-update-interval 0.5)
  :config
  (with-eval-after-load "evil-collection"
    (evil-collection-proced-setup)))
#+end_src
* Calculator
Surprise, surprise Emacs comes with a calculator.

Greater surprise, this thing is over powered.  It can perform the
following (and more):
- Matrix calculations
- Generalised calculus operations
- Equation solvers for n-degree multi-variable polynomials
- Embedded mode (check below)!

~calc-mode~ is a calculator system within Emacs that provides a
diverse array of mathematical operations.  It uses reverse polish
notation to do calculations (though there is a standard infix
algebraic notation mode).

Embedded mode allows computation with the current buffer as the echo
area.  This basically means I can compute stuff within a buffer
without invoking calc directly: $1 + 2\rightarrow_{\text{calc-embed}} 3$.

#+begin_src emacs-lisp
(use-package calc
  :straight nil
  :display
  ("*Calculator*"
   (display-buffer-at-bottom)
   (window-height . 0.18))
  :general
  (app-leader
    "c" #'calc-dispatch)
  (mode-leader
    "c" #'calc-embedded)
  :init
  (setq calc-algebraic-mode t)
  :config
  (with-eval-after-load "evil-collection"
    (evil-collection-calc-setup)))
#+end_src
** WAIT Calctex
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
~calc-mode~ also has a 3rd party package called ~calctex~. It renders
mathematical expressions within calc as if they were rendered in TeX.
You can also copy the expressions in their TeX forms, which is pretty
useful when writing a paper.  I've set a very specific lock on this
repository as it's got quite a messy work-tree and this commit seems to
work for me given the various TeX utilities installed via Arch.

#+begin_src emacs-lisp
(use-package calctex
  :after calc
  :straight (calctex :type git :host github :repo "johnbcoughlin/calctex")
  :hook (calc-mode-hook . calctex-mode))
#+end_src
* WAIT Ledger
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
#+begin_src emacs-lisp
(use-package ledger-mode
  :defer t)

(use-package evil-ledger
  :after ledger-mode)
#+end_src
* Zone
Of course Emacs has a cool screensaver software.

#+begin_src emacs-lisp
(use-package zone-matrix
  :straight t
  :commands (zone)
  :general
  (leader
    "z" #'zone)
  :init
  (setq zone-programs
        [zone-pgm-drip
         zone-pgm-drip-fretfully
         zone-pgm-martini-swan-dive
         zone-pgm-stress
         zone-pgm-random-life]))
#+end_src
* (Wo)man
Man pages are the user manuals for most software on Linux.  Really
useful when writing code for Un*x systems, though they can be very
verbose.

2023-08-17: `Man-notify-method' is the reason the `:display' record
doesn't work here.  I think it's to do with how Man pages are rendered
or something, but very annoying as it's a break from standards!
#+begin_src emacs-lisp
(use-package man
  :demand t
  :straight nil
  :init
  (setq Man-notify-method 'pushy)
  :display
  ("^\\*Man.*"
   (display-buffer-reuse-mode-window display-buffer-same-window))
  :general
  (file-leader
    "m" #'man) ;; kinda like "find man page"
  (nmmap
    :keymaps 'Man-mode-map
    "RET" #'man-follow))
#+end_src
* WAIT gif-screencast
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
Little application that uses =gifsicle= to make essentially videos of
Emacs.  Useful for demonstrating features.
#+begin_src emacs-lisp
(use-package gif-screencast
  :straight t
  :general
  (app-leader
    "x" #'gif-screencast-start-or-stop)
  :init
  (setq gif-screencast-output-directory (expand-file-name "~/Media/emacs/")))
#+end_src
* Image-mode
Image mode, for viewing images.  Supports tons of formats, easy to use
and integrates slickly into image-dired.  Of course,
#+begin_src emacs-lisp
(use-package image-mode
  :straight nil
  :general
  (nmmap
    :keymaps 'image-mode-map
    "+" #'image-increase-size
    "-" #'image-decrease-size
    "p" #'image-animate
    "P" #'image-animate-set-speed
    "h" #'image-backward-hscroll
    "j" #'image-next-line
    "k" #'image-previous-line
    "l" #'image-forward-hscroll))
#+end_src
* WAIT ERC
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
#+begin_src emacs-lisp
(use-package erc
  :defer t
  :init
  (setq erc-server "irc.libera.chat"
        erc-nick "oreodave"
        erc-buffer-display "current"))
#+end_src
* MPV
Basically a porcelain over mpv via the IPC interface.
#+begin_src emacs-lisp
(use-package mpv
  :defer t
  :straight t
  :config
  (with-eval-after-load "org"
    (defun org-mpv-complete-link (&optional arg)
      (replace-regexp-in-string
       "file:" "mpv:"
       (org-link-complete-file arg)
       t t))
    (org-link-set-parameters "mpv"
                             :follow #'mpv-play :complete #'org-mpv-complete-link)))
#+end_src