(Emacs)~config->app,core

The two largest sections of my config are separated into their own
files now.  Does increase init time, but I just can't handle how big
this thing is.  It'll be a bit nicer to look at and manage with
separate files.
This commit is contained in:
2023-09-29 22:30:58 +01:00
parent cc25dcf812
commit a0a5b2e05d
4 changed files with 1563 additions and 1518 deletions

787
Emacs/.config/emacs/app.org Normal file
View File

@@ -0,0 +1,787 @@
#+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.
* WIP 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)
: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/toggle-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))))))
(+oreo/create-toggle-function
+calendar/toggle-calendar
"*Calendar*"
calendar
nil))
#+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 (concat user-emacs-directory ".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 :before
#'+mail/sync-mail)
(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
:after notmuch
:commands mail-send
:custom
(smtpmail-smtp-server "mail.aryadevchavali.com")
(smtpmail-smtp-user "aryadev")
(smtpmail-smtp-service 587)
(smtpmail-stream-type 'starttls)
:init
(setq 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 "-AFBl --group-directories-first"
dired-omit-files "^\\."
dired-dwim-target t)
(with-eval-after-load "evil-collection"
(evil-collection-dired-setup))
:general
(nmmap
:keymaps 'dired-mode-map
"T" #'dired-create-empty-file
"H" #'dired-up-directory
"L" #'dired-find-file)
(dir-leader
"f" #'find-dired
"d" #'dired
"D" #'dired-other-frame
"i" #'image-dired
"p" `((proc (interactive)
(dired "~/Text/PDFs/"))
:which-key "Open PDFs"))
:config
(defun +dired/insert-all-subdirectories ()
"Insert all subdirectories currently viewable."
(interactive)
(dired-mark-directories nil)
(dolist #'dired-insert-subdir (dired-get-marked-files))
(dired-unmark-all-marks))
(nmmap
:keymaps 'dired-mode-map
"SPC" nil
"SPC ," nil)
(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)
(local-leader
:keymaps 'dired-mode-map
"l" #'dired-maybe-insert-subdir
"m" #'dired-mark-files-regexp
"u" #'dired-undo))
#+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
* Xwidget
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.
** 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, to name a few)
- 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 runs, you can 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
:commands +shell/toggle-eshell
:general
(shell-leader
"t" #'+shell/toggle-eshell)
:init
(add-hook
'eshell-mode-hook
(proc
(interactive)
(general-def
:states '(normal insert)
:keymaps 'eshell-mode-map
"M-l" (proc (interactive) (eshell/clear)
"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))))
:config
(+oreo/create-toggle-function
+shell/toggle-eshell
"*eshell*"
eshell
t))
#+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.25)))
#+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).
Also 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~).
#+begin_src emacs-lisp
(use-package eshell
:config
(defun +eshell/get-git-properties ()
(let* ((git-branch (shell-command-to-string "git branch"))
(is-repo (string= (if (string= git-branch "") ""
(substring git-branch 0 1)) "*")))
(if (not is-repo) ""
(concat
"("
(nth 2 (split-string git-branch "\n\\|\\*\\| "))
"<"
(if (string= "" (shell-command-to-string "git status | grep 'up to date'"))
"×"
"")
">)"))))
(setq eshell-cmpl-ignore-case t
eshell-cd-on-directory t
eshell-banner-message (concat (shell-command-to-string "figlet eshell") "\n")
eshell-prompt-function
(proc
(let ((properties (+eshell/get-git-properties)))
(concat
properties
(format "[%s]\n" (abbreviate-file-name (eshell/pwd)))
"λ ")))
eshell-prompt-regexp "")
(defun eshell/goto (&rest args)
"Use `read-directory-name' to change directories."
(eshell/cd (list (read-directory-name "Enter directory to go to:")))))
#+end_src
** Eshell change directory quickly
~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.
#+begin_src emacs-lisp
(use-package eshell
:straight nil
:general
(shell-leader
"T" #'+eshell/current-buffer)
:config
(defun +eshell/current-buffer ()
(interactive)
(let ((dir (if buffer-file-name
(file-name-directory buffer-file-name)
(if default-directory
default-directory
nil)))
(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
* Elfeed
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/"
Linux)
("LEMMiNO"
"https://www.youtube.com/feeds/videos.xml?channel_id=UCRcgy6GzDeccI7dkbbBna3Q"
YouTube Stories)
("The Onion"
"https://www.theonion.com/rss"
Social)
("Stack exchange"
"http://morss.aryadevchavali.com/stackexchange.com/feeds/questions"
Social)
("Dark Sominium"
"https://www.youtube.com/feeds/videos.xml?channel_id=UC_e39rWdkQqo5-LbiLiU10g"
YouTube Stories)
("Dark Sominium Music"
"https://www.youtube.com/feeds/videos.xml?channel_id=UCkLiZ_zLynyNd5fd62hg1Kw"
YouTube Music)
("Nexpo"
"https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw"
YouTube)
("Techquickie"
"https://www.youtube.com/feeds/videos.xml?channel_id=UC0vBXGSyV14uvJ4hECDOl0Q"
YouTube)
("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"
"http://morss.aryadevchavali.com/news.ycombinator.com/rss"
Social)
("Hacker Factor"
"https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2"
Social)
("BBC Top News"
"http://morss.aryadevchavali.com/feeds.bbci.co.uk/news/rss.xml"
News)
("BBC Tech News"
"http://morss.aryadevchavali.com/feeds.bbci.co.uk/news/technology/rss.xml"
News)))
: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)))
#+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")
"vf" '(magit-file-dispatch :which-key "Magit file")
"vb" '(magit-blame :which-key "Magit blame"))
(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)
: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
#+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
* Processes
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)
** Proced
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
Along with that I setup the package ~proced-narrow~ which allows
further filtering of the process list.
#+begin_src emacs-lisp
(use-package proced-narrow
:straight t
:after proced
:general
(nmap
:keymaps 'proced-mode-map
"%" #'proced-narrow))
#+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
** WIP 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
* Ledger
#+begin_src emacs-lisp
(use-package ledger-mode
:defer t)
(use-package evil-ledger
:after ledger-mode)
#+end_src
* WIP Zone
:PROPERTIES:
:header-args:emacs-lisp: :tangle no
:END:
Of course Emacs has a cool screensaver software.
#+begin_src emacs-lisp
(use-package zone-matrix
:straight t
:after dashboard
:init
(setq zone-programs
[zone-pgm-jitter
zone-pgm-putz-with-case
zone-pgm-dissolve
zone-pgm-whack-chars
zone-pgm-drip
zone-pgm-rat-race
zone-pgm-random-life
zone-matrix
])
:config
(zone-when-idle 15))
#+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
* gif-screencast
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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,738 @@
#+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 c" '(nil :which-key "Code")
"SPC f" '(nil :which-key "File")
"SPC t" '(nil :which-key "Shell")
"SPC m" '(nil :which-key "Toggle modes")
"SPC a" '(nil :which-key "Applications")
"SPC s" '(nil :which-key "Search")
"SPC b" '(nil :which-key "Buffers")
"SPC q" '(nil :which-key "Quit/Literate")
"SPC i" '(nil :which-key "Insert")
"SPC d" '(nil :which-key "Directories"))
(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)
(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

View File

@@ -33,7 +33,17 @@
(cons first (+literate/filter predicate rest))
(+literate/filter predicate rest)))))
(defconst +literate/org-files (list (concat user-emacs-directory "config.org")))
(defun +literate/org-p (filename)
(string= "org" (file-name-extension filename)))
(defun +literate/el-p (filename)
(string= "el" (file-name-extension filename)))
(defconst +literate/org-files
(+literate/filter
#'+literate/org-p
(mapcar #'(lambda (file) (concat user-emacs-directory file))
(cddr (directory-files user-emacs-directory)))))
(defconst +literate/output-files
(mapcar #'(lambda (x) (replace-regexp-in-string ".org" ".el" x)) +literate/org-files))
@@ -41,11 +51,12 @@
(defconst +literate/elisp-files
`(,(concat user-emacs-directory "early-init.el")
,(concat user-emacs-directory "init.el")
,@+literate/output-files
,@(mapcar
#'(lambda (name) (concat user-emacs-directory "elisp/" name))
;; Only take .el files
(+literate/filter
(lambda (name) (string= "el" (file-name-extension name)))
#'+literate/el-p
(cddr (directory-files (concat user-emacs-directory "elisp/")))))))
;; Setup predicates and loading
@@ -62,14 +73,15 @@
(file-exists-p (car +literate/output-files))))
(defun +literate/load-config ()
"Load all files in +literate/output-files."
"Load the first file in +literate/output-files."
(interactive)
(mapc #'(lambda (x) (load-file x)) +literate/output-files))
(load-file (concat user-emacs-directory "config.el")))
(autoload #'org-babel-tangle-file "ob-tangle")
(defun +literate/tangle-if-old (org-file)
(let ((output-file (replace-regexp-in-string ".org" ".el" org-file)))
(message "Tangle(%s)->%s" org-file output-file)
(if (or (not (file-exists-p output-file)) (file-newer-than-file-p org-file output-file))
(org-babel-tangle-file org-file))))
@@ -88,7 +100,7 @@
(message "Byte-compiling literate files...")
(mapc #'+literate/byte-compile-if-old +literate/output-files)
(message "Literate files byte-compiled")
(message "Byte compiling init.el, early-init.el, elisp/*")
(message "Byte compiling init.el, early-init.el, *.org~>*.el elisp/*")
(mapc #'+literate/byte-compile-if-old +literate/elisp-files)
(message "Finished byte-compiling"))