From a0a5b2e05d3beae7ed3d5ed14b6b65bbc811c7a9 Mon Sep 17 00:00:00 2001 From: Aryadev Chavali Date: Fri, 29 Sep 2023 22:30:58 +0100 Subject: (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. --- Emacs/.config/emacs/app.org | 787 +++++++++++++++++ Emacs/.config/emacs/config.org | 1534 +-------------------------------- Emacs/.config/emacs/core.org | 738 ++++++++++++++++ Emacs/.config/emacs/elisp/literate.el | 22 +- 4 files changed, 1563 insertions(+), 1518 deletions(-) create mode 100644 Emacs/.config/emacs/app.org create mode 100644 Emacs/.config/emacs/core.org (limited to 'Emacs/.config/emacs') diff --git a/Emacs/.config/emacs/app.org b/Emacs/.config/emacs/app.org new file mode 100644 index 0000000..46839d8 --- /dev/null +++ b/Emacs/.config/emacs/app.org @@ -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 " 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 + "" #'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 diff --git a/Emacs/.config/emacs/config.org b/Emacs/.config/emacs/config.org index 7e09fba..d2116e3 100644 --- a/Emacs/.config/emacs/config.org +++ b/Emacs/.config/emacs/config.org @@ -389,748 +389,12 @@ Eww who uses a mouse? #+begin_src emacs-lisp (setq use-file-dialog nil) #+end_src -* Core packages -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 - :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. +* Core packages (loading) +For my core packages, whose configuration doesn't change much anyway, +I have a [[file:core.org][separate file]]. Here I'll load it up for +usage later on. #+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 -** Licensing -Loads [[file:elisp/license.el][license.el]] for inserting licenses. -Licenses are important for distribution and attribution to be defined clearly. -#+begin_src emacs-lisp -(use-package license - :straight nil - :load-path "elisp/" - :demand t - :general - (insert-leader - "l" #'+license/insert-copyright-notice - "L" #'+license/insert-complete-license)) +(load-file (concat user-emacs-directory "core.el")) #+end_src * Small packages ** ISearch @@ -1460,784 +724,28 @@ text. That's what this is mainly for. (mode-leader "r" #'toggle-rot13-mode)) #+end_src -* Applications -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 " 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 - "" #'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$. - +** Licensing +Loads [[file:elisp/license.el][license.el]] for inserting licenses. +Licenses are important for distribution and attribution to be defined clearly. #+begin_src emacs-lisp -(use-package calc +(use-package license :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 + :load-path "elisp/" :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)) + (insert-leader + "l" #'+license/insert-copyright-notice + "L" #'+license/insert-complete-license)) #+end_src -** gif-screencast -Little application that uses =gifsicle= to make essentially videos of -Emacs. Useful for demonstrating features. +* Applications (loading) +Emacs is basically an operating system whose primary datatype is text. +Applications are interfaces/environments which serve a variety of +purposes, but provide a lot of capability. I have a +[[file:app.org][separate file]] for such configuration (2023-09-29: +mainly because it was so goddamn huge). + #+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/"))) +(load-file (concat user-emacs-directory "app.el")) #+end_src * Text modes Standard packages and configurations for text-mode and its derived diff --git a/Emacs/.config/emacs/core.org b/Emacs/.config/emacs/core.org new file mode 100644 index 0000000..687b226 --- /dev/null +++ b/Emacs/.config/emacs/core.org @@ -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 diff --git a/Emacs/.config/emacs/elisp/literate.el b/Emacs/.config/emacs/elisp/literate.el index 67ee88e..114c3bb 100644 --- a/Emacs/.config/emacs/elisp/literate.el +++ b/Emacs/.config/emacs/elisp/literate.el @@ -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")) -- cgit v1.2.3-13-gbd6f