(Emacs)~config->app,core
The two largest sections of my config are separated into their own files now. Does increase init time, but I just can't handle how big this thing is. It'll be a bit nicer to look at and manage with separate files.
This commit is contained in:
787
Emacs/.config/emacs/app.org
Normal file
787
Emacs/.config/emacs/app.org
Normal file
@@ -0,0 +1,787 @@
|
|||||||
|
#+title: Applications for Emacs
|
||||||
|
#+author: Aryadev Chavali
|
||||||
|
#+description: Applications for my Emacs OS™
|
||||||
|
#+date: 2023-09-29
|
||||||
|
#+property: header-args:emacs-lisp :tangle app.el :comments link :results none
|
||||||
|
#+options: toc:nil
|
||||||
|
#+startup: noindent
|
||||||
|
|
||||||
|
Applications are greater than packages; they provide a set of
|
||||||
|
functionality to create an interface in Emacs. Emacs comes with
|
||||||
|
applications and others may be installed.
|
||||||
|
|
||||||
|
* WIP Dashboard
|
||||||
|
:PROPERTIES:
|
||||||
|
:header-args:emacs-lisp: :tangle no
|
||||||
|
:END:
|
||||||
|
Dashboard creates a custom dashboard for Emacs that replaces the
|
||||||
|
initial startup screen in default Emacs. It has a lot of customising
|
||||||
|
options.
|
||||||
|
|
||||||
|
Unfortunately not that useful, many things are easier to invoke
|
||||||
|
directly such as recent files or project changing.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package dashboard
|
||||||
|
:straight t
|
||||||
|
:demand t
|
||||||
|
:general
|
||||||
|
(app-leader
|
||||||
|
"b" #'dashboard-refresh-buffer)
|
||||||
|
(:states '(normal motion emacs)
|
||||||
|
:keymaps 'dashboard-mode-map
|
||||||
|
"q" (proc (interactive) (kill-this-buffer)))
|
||||||
|
(nmmap
|
||||||
|
:keymaps 'dashboard-mode-map
|
||||||
|
"r" #'dashboard-jump-to-recent-files
|
||||||
|
"p" #'dashboard-jump-to-projects
|
||||||
|
"}" #'dashboard-next-section
|
||||||
|
"{" #'dashboard-previous-section)
|
||||||
|
:init
|
||||||
|
(setq initial-buffer-choice nil
|
||||||
|
dashboard-banner-logo-title "Oreomacs"
|
||||||
|
dashboard-center-content t
|
||||||
|
dashboard-set-init-info t
|
||||||
|
dashboard-startup-banner (no-littering-expand-etc-file-name "dashboard/logo.png")
|
||||||
|
dashboard-set-footer t
|
||||||
|
dashboard-set-navigator t
|
||||||
|
dashboard-items '((projects . 5)
|
||||||
|
(recents . 5))
|
||||||
|
dashboard-footer-messages (list
|
||||||
|
"Collecting parentheses..."
|
||||||
|
"Linking 'coffee_machine.o'..."
|
||||||
|
"Uploading ip to hacker named 4chan..."
|
||||||
|
"Dividing by zero..."
|
||||||
|
"Solving 3-sat..."
|
||||||
|
"Obtaining your health record..."
|
||||||
|
(format "Recompiling Emacs for the %dth time..." (random 1000))
|
||||||
|
"Escaping the cycle of samsara..."))
|
||||||
|
:config
|
||||||
|
(dashboard-setup-startup-hook))
|
||||||
|
#+end_src
|
||||||
|
* EWW
|
||||||
|
Emacs Web Wowser is the inbuilt text based web browser for Emacs. It
|
||||||
|
can render images and basic CSS styles but doesn't have a JavaScript
|
||||||
|
engine, which makes sense as it's primarily a text interface.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package eww
|
||||||
|
:defer t
|
||||||
|
:general
|
||||||
|
(app-leader
|
||||||
|
"w" #'eww)
|
||||||
|
:straight nil
|
||||||
|
:config
|
||||||
|
(with-eval-after-load "evil-collection"
|
||||||
|
(evil-collection-eww-setup)))
|
||||||
|
#+end_src
|
||||||
|
* Calendar
|
||||||
|
Calendar is a simple inbuilt application that helps with date
|
||||||
|
functionalities. I add functionality to copy dates from the calendar
|
||||||
|
to the kill ring and bind it to "Y".
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package calendar
|
||||||
|
:straight nil
|
||||||
|
:defer t
|
||||||
|
:commands (+calendar/copy-date +calendar/toggle-calendar)
|
||||||
|
:display
|
||||||
|
("\\*Calendar\\*"
|
||||||
|
(display-buffer-at-bottom)
|
||||||
|
(inhibit-duplicate-buffer . t)
|
||||||
|
(window-height . 0.17))
|
||||||
|
:general
|
||||||
|
(nmmap
|
||||||
|
:keymaps 'calendar-mode-map
|
||||||
|
"Y" #'+calendar/copy-date)
|
||||||
|
(app-leader
|
||||||
|
"d" #'+calendar/toggle-calendar)
|
||||||
|
:config
|
||||||
|
(defun +calendar/copy-date ()
|
||||||
|
"Copy date under cursor into kill ring."
|
||||||
|
(interactive)
|
||||||
|
(if (use-region-p)
|
||||||
|
(call-interactively #'kill-ring-save)
|
||||||
|
(let ((date (calendar-cursor-to-date)))
|
||||||
|
(when date
|
||||||
|
(setq date (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))
|
||||||
|
(kill-new (format-time-string "%Y-%m-%d" date))))))
|
||||||
|
(+oreo/create-toggle-function
|
||||||
|
+calendar/toggle-calendar
|
||||||
|
"*Calendar*"
|
||||||
|
calendar
|
||||||
|
nil))
|
||||||
|
#+end_src
|
||||||
|
* Mail
|
||||||
|
Mail is a funny thing; most people use it just for business or
|
||||||
|
advertising and it's come out of use in terms of personal
|
||||||
|
communication in the west for the most part (largely due to "social"
|
||||||
|
media applications). However, this isn't true for the open source and
|
||||||
|
free software movement who heavily use mail for communication.
|
||||||
|
|
||||||
|
Integrating mail into Emacs helps as I can send source code and
|
||||||
|
integrate it into my workflow just a bit better.
|
||||||
|
** Notmuch
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package notmuch
|
||||||
|
:defer t
|
||||||
|
:commands (notmuch +mail/flag-thread)
|
||||||
|
:general
|
||||||
|
(app-leader "m" #'notmuch)
|
||||||
|
(nmap
|
||||||
|
:keymaps 'notmuch-search-mode-map
|
||||||
|
"f" #'+mail/flag-thread)
|
||||||
|
:init
|
||||||
|
(defconst +mail/signature "---------------\nAryadev Chavali")
|
||||||
|
(defconst +mail/local-dir (concat user-emacs-directory ".mail/"))
|
||||||
|
(setq notmuch-show-logo nil
|
||||||
|
notmuch-search-oldest-first nil
|
||||||
|
notmuch-hello-sections '(notmuch-hello-insert-saved-searches
|
||||||
|
notmuch-hello-insert-alltags
|
||||||
|
notmuch-hello-insert-recent-searches)
|
||||||
|
notmuch-archive-tags '("-inbox" "-unread" "+archive")
|
||||||
|
mail-signature +mail/signature
|
||||||
|
mail-default-directory +mail/local-dir
|
||||||
|
mail-source-directory +mail/local-dir
|
||||||
|
message-signature +mail/signature
|
||||||
|
message-auto-save-directory +mail/local-dir
|
||||||
|
message-directory +mail/local-dir)
|
||||||
|
|
||||||
|
(defun +mail/sync-mail ()
|
||||||
|
"Sync mail via mbsync."
|
||||||
|
(interactive)
|
||||||
|
(start-process-shell-command "" nil "mbsync -a"))
|
||||||
|
(defun +mail/trash-junk ()
|
||||||
|
"Delete any mail in junk"
|
||||||
|
(interactive)
|
||||||
|
(start-process-shell-command "" nil "notmuch search --output=files --format=text0 tag:deleted tag:spam tag:trash tag:junk | xargs -r0 rm"))
|
||||||
|
:config
|
||||||
|
(defun +mail/flag-thread (&optional unflag beg end)
|
||||||
|
(interactive (cons current-prefix-arg (notmuch-interactive-region)))
|
||||||
|
(notmuch-search-tag
|
||||||
|
(notmuch-tag-change-list '("-inbox" "+flagged") unflag) beg end)
|
||||||
|
(when (eq beg end)
|
||||||
|
(notmuch-search-next-thread)))
|
||||||
|
(advice-add #'notmuch-poll-and-refresh-this-buffer :before
|
||||||
|
#'+mail/sync-mail)
|
||||||
|
(advice-add #'notmuch-poll-and-refresh-this-buffer :after
|
||||||
|
#'+mail/trash-junk)
|
||||||
|
(with-eval-after-load "evil-collection"
|
||||||
|
(evil-collection-notmuch-setup)))
|
||||||
|
#+end_src
|
||||||
|
** Smtpmail
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package smtpmail
|
||||||
|
:after notmuch
|
||||||
|
:commands mail-send
|
||||||
|
:custom
|
||||||
|
(smtpmail-smtp-server "mail.aryadevchavali.com")
|
||||||
|
(smtpmail-smtp-user "aryadev")
|
||||||
|
(smtpmail-smtp-service 587)
|
||||||
|
(smtpmail-stream-type 'starttls)
|
||||||
|
:init
|
||||||
|
(setq send-mail-function #'smtpmail-send-it
|
||||||
|
message-send-mail-function #'smtpmail-send-it))
|
||||||
|
#+end_src
|
||||||
|
* Dired
|
||||||
|
Setup for dired. Make dired-hide-details-mode the default mode when
|
||||||
|
using dired-mode, as it removes the clutter. Setup evil collection
|
||||||
|
for dired (even though dired doesn't really conflict with evil, there
|
||||||
|
are some corners I'd like to adjust).
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package dired
|
||||||
|
:straight nil
|
||||||
|
:commands (dired find-dired)
|
||||||
|
:hook
|
||||||
|
(dired-mode-hook . auto-revert-mode)
|
||||||
|
(dired-mode-hook . dired-hide-details-mode)
|
||||||
|
:init
|
||||||
|
(setq-default dired-listing-switches "-AFBl --group-directories-first"
|
||||||
|
dired-omit-files "^\\."
|
||||||
|
dired-dwim-target t)
|
||||||
|
(with-eval-after-load "evil-collection"
|
||||||
|
(evil-collection-dired-setup))
|
||||||
|
:general
|
||||||
|
(nmmap
|
||||||
|
:keymaps 'dired-mode-map
|
||||||
|
"T" #'dired-create-empty-file
|
||||||
|
"H" #'dired-up-directory
|
||||||
|
"L" #'dired-find-file)
|
||||||
|
(dir-leader
|
||||||
|
"f" #'find-dired
|
||||||
|
"d" #'dired
|
||||||
|
"D" #'dired-other-frame
|
||||||
|
"i" #'image-dired
|
||||||
|
"p" `((proc (interactive)
|
||||||
|
(dired "~/Text/PDFs/"))
|
||||||
|
:which-key "Open PDFs"))
|
||||||
|
:config
|
||||||
|
(defun +dired/insert-all-subdirectories ()
|
||||||
|
"Insert all subdirectories currently viewable."
|
||||||
|
(interactive)
|
||||||
|
(dired-mark-directories nil)
|
||||||
|
(dolist #'dired-insert-subdir (dired-get-marked-files))
|
||||||
|
(dired-unmark-all-marks))
|
||||||
|
|
||||||
|
(nmmap
|
||||||
|
:keymaps 'dired-mode-map
|
||||||
|
"SPC" nil
|
||||||
|
"SPC ," nil)
|
||||||
|
|
||||||
|
(nmmap
|
||||||
|
:keymaps 'image-dired-thumbnail-mode-map
|
||||||
|
"h" #'image-dired-backward-image
|
||||||
|
"l" #'image-dired-forward-image
|
||||||
|
"j" #'image-dired-next-line
|
||||||
|
"k" #'image-dired-previous-line
|
||||||
|
"H" #'image-dired-display-previous
|
||||||
|
"L" #'image-dired-display-next
|
||||||
|
"RET" #'image-dired-display-this
|
||||||
|
"m" #'image-dired-mark-thumb-original-file
|
||||||
|
"q" #'quit-window)
|
||||||
|
|
||||||
|
(local-leader
|
||||||
|
:keymaps 'dired-mode-map
|
||||||
|
"l" #'dired-maybe-insert-subdir
|
||||||
|
"m" #'dired-mark-files-regexp
|
||||||
|
"u" #'dired-undo))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
** fd-dired
|
||||||
|
Uses fd for finding file results in a directory: ~find-dired~ ->
|
||||||
|
~fd-dired~.
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package fd-dired
|
||||||
|
:after dired
|
||||||
|
:straight t
|
||||||
|
:general
|
||||||
|
(dir-leader
|
||||||
|
"g" #'fd-dired))
|
||||||
|
#+end_src
|
||||||
|
* Xwidget
|
||||||
|
Xwidget is a package which allows for the insertion of arbitrary
|
||||||
|
xwidgets into Emacs through buffers. It must be compiled into Emacs
|
||||||
|
so you might need to customise your install. One of its premier uses
|
||||||
|
is in navigating the web which it provides through the function
|
||||||
|
~xwidget-webkit-browse-url~. This renders a fully functional web
|
||||||
|
browser within Emacs.
|
||||||
|
|
||||||
|
Though I am not to keen on using Emacs to browse the web /via/ xwidget
|
||||||
|
(EWW does a good job on its own), I am very interested in its
|
||||||
|
capability to render pages with JavaScript, as it may come of use when
|
||||||
|
doing web development. I can see the results of work very quickly
|
||||||
|
without switching windows all within Emacs.
|
||||||
|
** Xwidget Core
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package xwidget
|
||||||
|
:straight nil
|
||||||
|
:general
|
||||||
|
(app-leader
|
||||||
|
"u" #'xwidget-webkit-browse-url)
|
||||||
|
(nmmap
|
||||||
|
:keymaps 'xwidget-webkit-mode-map
|
||||||
|
"q" #'quit-window
|
||||||
|
"h" #'xwidget-webkit-scroll-backward
|
||||||
|
"j" #'xwidget-webkit-scroll-up
|
||||||
|
"k" #'xwidget-webkit-scroll-down
|
||||||
|
"l" #'xwidget-webkit-scroll-forward
|
||||||
|
"+" #'xwidget-webkit-zoom-in
|
||||||
|
"-" #'xwidget-webkit-zoom-out
|
||||||
|
(kbd "C-f") #'xwidget-webkit-scroll-up
|
||||||
|
(kbd "C-b") #'xwidget-webkit-scroll-down
|
||||||
|
"H" #'xwidget-webkit-back
|
||||||
|
"L" #'xwidget-webkit-forward
|
||||||
|
"gu" #'xwidget-webkit-browse-url
|
||||||
|
"gr" #'xwidget-webkit-reload
|
||||||
|
"gg" #'xwidget-webkit-scroll-top
|
||||||
|
"G" #'xwidget-webkit-scroll-bottom))
|
||||||
|
#+end_src
|
||||||
|
** Xwidget Extensions
|
||||||
|
Define a function ~+xwidget/render-file~ that reads a file name and
|
||||||
|
presents it in an xwidget. If the current file is an HTML file, ask
|
||||||
|
if user wants to open current file. Bind it to ~aU~ in the leader.
|
||||||
|
|
||||||
|
Also define a function ~+xwidget/search-query~ that first asks the
|
||||||
|
user what search engine they want to use ([[https://duckduckgo.com][Duck Duck Go]] and [[https://devdocs.io][DevDocs]]
|
||||||
|
currently) then asks for a query, which it parses then presents in an
|
||||||
|
xwidget window. Bind to ~as~ in the leader.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package xwidget
|
||||||
|
:straight nil
|
||||||
|
:commands (+xwidget/render-file +xwidget/search)
|
||||||
|
:general
|
||||||
|
(app-leader
|
||||||
|
"U" #'+xwidget/render-file
|
||||||
|
"s" #'+xwidget/search)
|
||||||
|
:config
|
||||||
|
(setenv "WEBKIT_FORCE_SANDBOX" "0")
|
||||||
|
(defun +xwidget/render-file (&optional FORCE)
|
||||||
|
"Find file (or use current file) and render in xwidget."
|
||||||
|
(interactive)
|
||||||
|
(cond
|
||||||
|
((and (not FORCE) (or (string= (replace-regexp-in-string ".*.html"
|
||||||
|
"html" (buffer-name)) "html")
|
||||||
|
(eq major-mode 'web-mode)
|
||||||
|
(eq major-mode 'html-mode))) ; If in html file
|
||||||
|
(if (y-or-n-p "Open current file?: ") ; Maybe they want to open a separate file
|
||||||
|
(xwidget-webkit-browse-url (format "file://%s" (buffer-file-name)))
|
||||||
|
(+xwidget/render-file t))) ; recurse and open file via prompt
|
||||||
|
(t
|
||||||
|
(xwidget-webkit-browse-url
|
||||||
|
(format "file://%s" (read-file-name "Enter file to open: "))))))
|
||||||
|
|
||||||
|
(defun +xwidget/search ()
|
||||||
|
"Run a search query on some search engine and display in
|
||||||
|
xwidget."
|
||||||
|
(interactive)
|
||||||
|
(let* ((engine (completing-read "Engine: " '("duckduckgo.com" "devdocs.io") nil t))
|
||||||
|
(query-raw (read-string "Enter query: "))
|
||||||
|
(query
|
||||||
|
(cond
|
||||||
|
((string= engine "duckduckgo.com") query-raw)
|
||||||
|
((string= engine "devdocs.io") (concat "_ " query-raw)))))
|
||||||
|
(xwidget-webkit-browse-url (concat "https://" engine "/?q=" query)))))
|
||||||
|
#+end_src
|
||||||
|
* Eshell
|
||||||
|
** Why Eshell?
|
||||||
|
Eshell is an integrated shell environment for Emacs, written in Emacs
|
||||||
|
Lisp. I argue that it is the best shell/command interpreter to use in
|
||||||
|
Emacs.
|
||||||
|
|
||||||
|
Eshell is unlike the alternatives in Emacs as it's a /shell/ first,
|
||||||
|
not a terminal emulator. It has the ability to spoof some aspects of a
|
||||||
|
terminal emulator (through the shell parser), but it is NOT a terminal
|
||||||
|
emulator.
|
||||||
|
|
||||||
|
The killer benefits of eshell (which would appeal to Emacs users) are
|
||||||
|
a direct result of eshell being written in Emacs lisp:
|
||||||
|
- incredible integration with Emacs utilities (such as ~dired~,
|
||||||
|
~find-file~, any read functions, to name a few)
|
||||||
|
- very extensible, easy to write new commands which leverage Emacs
|
||||||
|
commands as well as external utilities
|
||||||
|
- agnostic of platform: "eshell/cd" will call the underlying change
|
||||||
|
directory function for you, so commands will (usually) mean the same
|
||||||
|
thing regardless of platform
|
||||||
|
- this means as long as Emacs runs, you can run eshell
|
||||||
|
|
||||||
|
However, my favourite feature of eshell is the set of evaluators that
|
||||||
|
run on command input. Some of the benefits listed above come as a
|
||||||
|
result of this powerful feature. These evaluators are described below.
|
||||||
|
|
||||||
|
Lisp evaluator: works on braced expressions, evaluating them as Lisp
|
||||||
|
expressions (e.g. ~(message "Hello, World!\n")~). Any returned
|
||||||
|
objects are printed. This makes eshell a LISP REPL!
|
||||||
|
|
||||||
|
External evaluator: works within curly braces, evaluating them via
|
||||||
|
some external shell process (like sh) (e.g. ~{echo "Hello,
|
||||||
|
world!\n"}~). This makes eshell a (kinda dumb) terminal emulator!
|
||||||
|
|
||||||
|
The alias evaluator is the top level evaluator. It is the main
|
||||||
|
evaluator for each expression given to eshell. When given an
|
||||||
|
expression it tries to evaluate it by testing against these conditions:
|
||||||
|
- it's an alias defined by the user or in the ~eshell/~ namespace of
|
||||||
|
functions (simplest evaluator)
|
||||||
|
- it's some form of lisp expression (lisp evaluator)
|
||||||
|
- it's an external command (bash evaluator)
|
||||||
|
Essentially, you get the best of both Emacs and external shell
|
||||||
|
programs *ALL WITHIN* Emacs for free.
|
||||||
|
** Eshell functionality
|
||||||
|
Bind some evil-like movements for easy shell usage, and a toggle
|
||||||
|
function to pull up the eshell quickly.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package eshell
|
||||||
|
:commands +shell/toggle-eshell
|
||||||
|
:general
|
||||||
|
(shell-leader
|
||||||
|
"t" #'+shell/toggle-eshell)
|
||||||
|
:init
|
||||||
|
(add-hook
|
||||||
|
'eshell-mode-hook
|
||||||
|
(proc
|
||||||
|
(interactive)
|
||||||
|
(general-def
|
||||||
|
:states '(normal insert)
|
||||||
|
:keymaps 'eshell-mode-map
|
||||||
|
"M-l" (proc (interactive) (eshell/clear)
|
||||||
|
"M-j" #'eshell-next-matching-input-from-input
|
||||||
|
"M-k" #'eshell-previous-matching-input-from-input)
|
||||||
|
(local-leader
|
||||||
|
:keymaps 'eshell-mode-map
|
||||||
|
"c" (proc (interactive) (eshell/clear)
|
||||||
|
(recenter))
|
||||||
|
"k" #'eshell-kill-process))))
|
||||||
|
:config
|
||||||
|
(+oreo/create-toggle-function
|
||||||
|
+shell/toggle-eshell
|
||||||
|
"*eshell*"
|
||||||
|
eshell
|
||||||
|
t))
|
||||||
|
#+end_src
|
||||||
|
** Eshell pretty symbols and display
|
||||||
|
Pretty symbols and a display record.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package eshell
|
||||||
|
:defer t
|
||||||
|
:pretty
|
||||||
|
(eshell-mode-hook
|
||||||
|
("lambda" . "λ")
|
||||||
|
("numberp" . "ℤ")
|
||||||
|
("t" . "⊨")
|
||||||
|
("nil" . "Ø"))
|
||||||
|
:display
|
||||||
|
("\\*e?shell\\*" ; for general shells as well
|
||||||
|
(display-buffer-at-bottom)
|
||||||
|
(window-height . 0.25)))
|
||||||
|
#+end_src
|
||||||
|
** Eshell variables and aliases
|
||||||
|
Set some sane defaults, a banner and a prompt. The prompt checks for
|
||||||
|
a git repo in the current directory and provides some extra
|
||||||
|
information in that case (in particular, branch name and if there any
|
||||||
|
changes that haven't been committed).
|
||||||
|
|
||||||
|
Also add ~eshell/goto~, which is actually a command accessible from
|
||||||
|
within eshell (this is because ~eshell/*~ creates an accessible
|
||||||
|
function within eshell with name ~*~). ~eshell/goto~ makes it easier
|
||||||
|
to change directories by using Emacs' find-file interface (which is
|
||||||
|
much faster than ~cd ..; ls -l~).
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package eshell
|
||||||
|
:config
|
||||||
|
(defun +eshell/get-git-properties ()
|
||||||
|
(let* ((git-branch (shell-command-to-string "git branch"))
|
||||||
|
(is-repo (string= (if (string= git-branch "") ""
|
||||||
|
(substring git-branch 0 1)) "*")))
|
||||||
|
(if (not is-repo) ""
|
||||||
|
(concat
|
||||||
|
"("
|
||||||
|
(nth 2 (split-string git-branch "\n\\|\\*\\| "))
|
||||||
|
"<"
|
||||||
|
(if (string= "" (shell-command-to-string "git status | grep 'up to date'"))
|
||||||
|
"×"
|
||||||
|
"✓")
|
||||||
|
">)"))))
|
||||||
|
(setq eshell-cmpl-ignore-case t
|
||||||
|
eshell-cd-on-directory t
|
||||||
|
eshell-banner-message (concat (shell-command-to-string "figlet eshell") "\n")
|
||||||
|
eshell-prompt-function
|
||||||
|
(proc
|
||||||
|
(let ((properties (+eshell/get-git-properties)))
|
||||||
|
(concat
|
||||||
|
properties
|
||||||
|
(format "[%s]\n" (abbreviate-file-name (eshell/pwd)))
|
||||||
|
"λ ")))
|
||||||
|
eshell-prompt-regexp "^λ ")
|
||||||
|
|
||||||
|
(defun eshell/goto (&rest args)
|
||||||
|
"Use `read-directory-name' to change directories."
|
||||||
|
(eshell/cd (list (read-directory-name "Enter directory to go to:")))))
|
||||||
|
#+end_src
|
||||||
|
** Eshell change directory quickly
|
||||||
|
~eshell/goto~ is a better ~cd~ for eshell. However it is really just
|
||||||
|
a plaster over a bigger issue for my workflow; many times I want
|
||||||
|
eshell to be present in the current directory of the buffer I am
|
||||||
|
using.
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package eshell
|
||||||
|
:straight nil
|
||||||
|
:general
|
||||||
|
(shell-leader
|
||||||
|
"T" #'+eshell/current-buffer)
|
||||||
|
:config
|
||||||
|
(defun +eshell/current-buffer ()
|
||||||
|
(interactive)
|
||||||
|
(let ((dir (if buffer-file-name
|
||||||
|
(file-name-directory buffer-file-name)
|
||||||
|
(if default-directory
|
||||||
|
default-directory
|
||||||
|
nil)))
|
||||||
|
(buf (eshell)))
|
||||||
|
(if dir
|
||||||
|
(with-current-buffer buf
|
||||||
|
(eshell/cd dir)
|
||||||
|
(eshell-send-input))
|
||||||
|
(message "Could not switch eshell: buffer is not real file")))))
|
||||||
|
#+end_src
|
||||||
|
* Elfeed
|
||||||
|
Elfeed is the perfect RSS feed reader, integrated into Emacs
|
||||||
|
perfectly. I've got a set of feeds that I use for a large variety of
|
||||||
|
stuff, mostly media and entertainment. I've also bound "<leader> ar"
|
||||||
|
to elfeed for loading the system.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package elfeed
|
||||||
|
:general
|
||||||
|
(app-leader "r" #'elfeed)
|
||||||
|
(nmmap
|
||||||
|
:keymaps 'elfeed-search-mode-map
|
||||||
|
"gr" #'elfeed-update
|
||||||
|
"s" #'elfeed-search-live-filter
|
||||||
|
"<return>" #'elfeed-search-show-entry)
|
||||||
|
:init
|
||||||
|
(setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/"))
|
||||||
|
(setq +rss/feed-urls
|
||||||
|
'(("Arch Linux"
|
||||||
|
"https://www.archlinux.org/feeds/news/"
|
||||||
|
Linux)
|
||||||
|
("LEMMiNO"
|
||||||
|
"https://www.youtube.com/feeds/videos.xml?channel_id=UCRcgy6GzDeccI7dkbbBna3Q"
|
||||||
|
YouTube Stories)
|
||||||
|
("The Onion"
|
||||||
|
"https://www.theonion.com/rss"
|
||||||
|
Social)
|
||||||
|
("Stack exchange"
|
||||||
|
"http://morss.aryadevchavali.com/stackexchange.com/feeds/questions"
|
||||||
|
Social)
|
||||||
|
("Dark Sominium"
|
||||||
|
"https://www.youtube.com/feeds/videos.xml?channel_id=UC_e39rWdkQqo5-LbiLiU10g"
|
||||||
|
YouTube Stories)
|
||||||
|
("Dark Sominium Music"
|
||||||
|
"https://www.youtube.com/feeds/videos.xml?channel_id=UCkLiZ_zLynyNd5fd62hg1Kw"
|
||||||
|
YouTube Music)
|
||||||
|
("Nexpo"
|
||||||
|
"https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw"
|
||||||
|
YouTube)
|
||||||
|
("Techquickie"
|
||||||
|
"https://www.youtube.com/feeds/videos.xml?channel_id=UC0vBXGSyV14uvJ4hECDOl0Q"
|
||||||
|
YouTube)
|
||||||
|
("3B1B"
|
||||||
|
"https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw"
|
||||||
|
YouTube)
|
||||||
|
("Fredrik Knusden"
|
||||||
|
"https://www.youtube.com/feeds/videos.xml?channel_id=UCbWcXB0PoqOsAvAdfzWMf0w"
|
||||||
|
YouTube Stories)
|
||||||
|
("Barely Sociable"
|
||||||
|
"https://www.youtube.com/feeds/videos.xml?channel_id=UC9PIn6-XuRKZ5HmYeu46AIw"
|
||||||
|
YouTube Stories)
|
||||||
|
("Atrocity Guide"
|
||||||
|
"https://www.youtube.com/feeds/videos.xml?channel_id=UCn8OYopT9e8tng-CGEWzfmw"
|
||||||
|
YouTube Stories)
|
||||||
|
("Hacker News"
|
||||||
|
"http://morss.aryadevchavali.com/news.ycombinator.com/rss"
|
||||||
|
Social)
|
||||||
|
("Hacker Factor"
|
||||||
|
"https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2"
|
||||||
|
Social)
|
||||||
|
("BBC Top News"
|
||||||
|
"http://morss.aryadevchavali.com/feeds.bbci.co.uk/news/rss.xml"
|
||||||
|
News)
|
||||||
|
("BBC Tech News"
|
||||||
|
"http://morss.aryadevchavali.com/feeds.bbci.co.uk/news/technology/rss.xml"
|
||||||
|
News)))
|
||||||
|
:config
|
||||||
|
(with-eval-after-load "evil-collection"
|
||||||
|
(evil-collection-elfeed-setup))
|
||||||
|
(setq elfeed-feeds (cl-map 'list #'(lambda (item)
|
||||||
|
(append (list (nth 1 item)) (cdr (cdr item))))
|
||||||
|
+rss/feed-urls)))
|
||||||
|
#+end_src
|
||||||
|
* Magit
|
||||||
|
Magit is *the* git porcelain for Emacs, which perfectly encapsulates
|
||||||
|
the git cli. In this case I just need to setup the bindings for it.
|
||||||
|
As magit will definitely load after evil (as it must be run by a
|
||||||
|
binding, and evil will load after init), I can use evil-collection
|
||||||
|
freely. Also, define an auto insert for commit messages so that I
|
||||||
|
don't need to write everything myself.
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package magit
|
||||||
|
:defer t
|
||||||
|
:display
|
||||||
|
("magit:.*"
|
||||||
|
(display-buffer-same-window)
|
||||||
|
(inhibit-duplicate-buffer . t))
|
||||||
|
("magit-diff:.*"
|
||||||
|
(display-buffer-below-selected))
|
||||||
|
("magit-log:.*"
|
||||||
|
(display-buffer-same-window))
|
||||||
|
:general
|
||||||
|
(leader
|
||||||
|
"g" '(magit-dispatch :which-key "Magit")
|
||||||
|
"vf" '(magit-file-dispatch :which-key "Magit file")
|
||||||
|
"vb" '(magit-blame :which-key "Magit blame"))
|
||||||
|
(code-leader
|
||||||
|
"b" #'magit-blame)
|
||||||
|
:auto-insert
|
||||||
|
(("COMMIT_EDITMSG" . "Commit skeleton")
|
||||||
|
""
|
||||||
|
"(" (read-string "Enter feature/module: ") ")"
|
||||||
|
(read-string "Enter simple description: ") "\n\n")
|
||||||
|
:init
|
||||||
|
(setq vc-follow-symlinks t
|
||||||
|
magit-blame-echo-style 'lines)
|
||||||
|
:config
|
||||||
|
(with-eval-after-load "evil"
|
||||||
|
(evil-set-initial-state 'magit-status-mode 'motion))
|
||||||
|
(with-eval-after-load "evil-collection"
|
||||||
|
(evil-collection-magit-setup)))
|
||||||
|
#+end_src
|
||||||
|
* IBuffer
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package ibuffer
|
||||||
|
:general
|
||||||
|
(buffer-leader
|
||||||
|
"i" #'ibuffer)
|
||||||
|
:config
|
||||||
|
(with-eval-after-load "evil-collection"
|
||||||
|
(evil-collection-ibuffer-setup)))
|
||||||
|
#+end_src
|
||||||
|
* Processes
|
||||||
|
Emacs has two systems for process management:
|
||||||
|
+ proced: a general 'top' like interface which allows general
|
||||||
|
management of linux processes
|
||||||
|
+ list-processes: a specific Emacs based system that lists processes
|
||||||
|
spawned by Emacs (similar to a top for Emacs specifically)
|
||||||
|
|
||||||
|
** Proced
|
||||||
|
Core proced config, just a few bindings and evil collection setup.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package proced
|
||||||
|
:straight nil
|
||||||
|
:general
|
||||||
|
(app-leader
|
||||||
|
"p" #'proced)
|
||||||
|
(nmap
|
||||||
|
:keymaps 'proced-mode-map
|
||||||
|
"za" #'proced-toggle-auto-update)
|
||||||
|
:display
|
||||||
|
("\\*Proced\\*"
|
||||||
|
(display-buffer-at-bottom)
|
||||||
|
(window-height . 0.25))
|
||||||
|
:init
|
||||||
|
(setq proced-auto-update-interval 0.5)
|
||||||
|
:config
|
||||||
|
(with-eval-after-load "evil-collection"
|
||||||
|
(evil-collection-proced-setup)))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Along with that I setup the package ~proced-narrow~ which allows
|
||||||
|
further filtering of the process list.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package proced-narrow
|
||||||
|
:straight t
|
||||||
|
:after proced
|
||||||
|
:general
|
||||||
|
(nmap
|
||||||
|
:keymaps 'proced-mode-map
|
||||||
|
"%" #'proced-narrow))
|
||||||
|
#+end_src
|
||||||
|
* Calculator
|
||||||
|
Surprise, surprise Emacs comes with a calculator.
|
||||||
|
|
||||||
|
Greater surprise, this thing is over powered. It can perform the
|
||||||
|
following (and more):
|
||||||
|
- Matrix calculations
|
||||||
|
- Generalised calculus operations
|
||||||
|
- Equation solvers for n-degree multi-variable polynomials
|
||||||
|
- Embedded mode (check below)!
|
||||||
|
|
||||||
|
~calc-mode~ is a calculator system within Emacs that provides a
|
||||||
|
diverse array of mathematical operations. It uses reverse polish
|
||||||
|
notation to do calculations (though there is a standard infix
|
||||||
|
algebraic notation mode).
|
||||||
|
|
||||||
|
Embedded mode allows computation with the current buffer as the echo
|
||||||
|
area. This basically means I can compute stuff within a buffer
|
||||||
|
without invoking calc directly: $1 + 2\rightarrow_{\text{calc-embed}} 3$.
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package calc
|
||||||
|
:straight nil
|
||||||
|
:display
|
||||||
|
("*Calculator*"
|
||||||
|
(display-buffer-at-bottom)
|
||||||
|
(window-height . 0.18))
|
||||||
|
:general
|
||||||
|
(app-leader
|
||||||
|
"c" #'calc-dispatch)
|
||||||
|
(mode-leader
|
||||||
|
"c" #'calc-embedded)
|
||||||
|
:init
|
||||||
|
(setq calc-algebraic-mode t)
|
||||||
|
:config
|
||||||
|
(with-eval-after-load "evil-collection"
|
||||||
|
(evil-collection-calc-setup)))
|
||||||
|
#+end_src
|
||||||
|
** WIP Calctex
|
||||||
|
:PROPERTIES:
|
||||||
|
:header-args:emacs-lisp: :tangle no
|
||||||
|
:END:
|
||||||
|
~calc-mode~ also has a 3rd party package called ~calctex~. It renders
|
||||||
|
mathematical expressions within calc as if they were rendered in TeX.
|
||||||
|
You can also copy the expressions in their TeX forms, which is pretty
|
||||||
|
useful when writing a paper. I've set a very specific lock on this
|
||||||
|
repository as it's got quite a messy work-tree and this commit seems to
|
||||||
|
work for me given the various TeX utilities installed via Arch.
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package calctex
|
||||||
|
:after calc
|
||||||
|
:straight (calctex :type git :host github :repo "johnbcoughlin/calctex")
|
||||||
|
:hook (calc-mode-hook . calctex-mode))
|
||||||
|
#+end_src
|
||||||
|
* Ledger
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package ledger-mode
|
||||||
|
:defer t)
|
||||||
|
|
||||||
|
(use-package evil-ledger
|
||||||
|
:after ledger-mode)
|
||||||
|
#+end_src
|
||||||
|
* WIP Zone
|
||||||
|
:PROPERTIES:
|
||||||
|
:header-args:emacs-lisp: :tangle no
|
||||||
|
:END:
|
||||||
|
Of course Emacs has a cool screensaver software.
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package zone-matrix
|
||||||
|
:straight t
|
||||||
|
:after dashboard
|
||||||
|
:init
|
||||||
|
(setq zone-programs
|
||||||
|
[zone-pgm-jitter
|
||||||
|
zone-pgm-putz-with-case
|
||||||
|
zone-pgm-dissolve
|
||||||
|
zone-pgm-whack-chars
|
||||||
|
zone-pgm-drip
|
||||||
|
zone-pgm-rat-race
|
||||||
|
zone-pgm-random-life
|
||||||
|
zone-matrix
|
||||||
|
])
|
||||||
|
:config
|
||||||
|
(zone-when-idle 15))
|
||||||
|
#+end_src
|
||||||
|
* (Wo)man
|
||||||
|
Man pages are the user manuals for most software on Linux. Really
|
||||||
|
useful when writing code for Un*x systems, though they can be very
|
||||||
|
verbose.
|
||||||
|
|
||||||
|
2023-08-17: `Man-notify-method' is the reason the `:display' record
|
||||||
|
doesn't work here. I think it's to do with how Man pages are rendered
|
||||||
|
or something, but very annoying as it's a break from standards!
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package man
|
||||||
|
:demand t
|
||||||
|
:straight nil
|
||||||
|
:init
|
||||||
|
(setq Man-notify-method 'pushy)
|
||||||
|
:display
|
||||||
|
("^\\*Man.*"
|
||||||
|
(display-buffer-reuse-mode-window display-buffer-same-window))
|
||||||
|
:general
|
||||||
|
(file-leader
|
||||||
|
"m" #'man) ;; kinda like "find man page"
|
||||||
|
(nmmap
|
||||||
|
:keymaps 'Man-mode-map
|
||||||
|
"RET" #'man-follow))
|
||||||
|
#+end_src
|
||||||
|
* gif-screencast
|
||||||
|
Little application that uses =gifsicle= to make essentially videos of
|
||||||
|
Emacs. Useful for demonstrating features.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package gif-screencast
|
||||||
|
:straight t
|
||||||
|
:general
|
||||||
|
(app-leader
|
||||||
|
"x" #'gif-screencast-start-or-stop)
|
||||||
|
:init
|
||||||
|
(setq gif-screencast-output-directory (expand-file-name "~/Media/emacs/")))
|
||||||
|
#+end_src
|
||||||
File diff suppressed because it is too large
Load Diff
738
Emacs/.config/emacs/core.org
Normal file
738
Emacs/.config/emacs/core.org
Normal file
@@ -0,0 +1,738 @@
|
|||||||
|
#+title: Core packages
|
||||||
|
#+author: Aryadev Chavali
|
||||||
|
#+description: The core components of my configuration
|
||||||
|
#+date: 2023-09-29
|
||||||
|
#+property: header-args:emacs-lisp :tangle core.el :comments link :results none
|
||||||
|
#+options: toc:nil
|
||||||
|
#+startup: noindent
|
||||||
|
|
||||||
|
Packages that are absolutely necessary for the rest of the
|
||||||
|
configuration. These yield core functionality such as keybinding,
|
||||||
|
modal editing, completion, auto typing to name a few.
|
||||||
|
* General
|
||||||
|
General provides a great solution for binding keys. It has evil and
|
||||||
|
use-package support so it fits nicely into configuration. In this
|
||||||
|
case, I define a "definer" for the "LEADER" keys. Leader is bound to
|
||||||
|
~SPC~ and it's functionally equivalent to the doom/spacemacs leader.
|
||||||
|
Local leader is bound to ~SPC ,~ and it's similar to doom/spacemacs
|
||||||
|
leader but doesn't try to fully assimilate the local-leader map,
|
||||||
|
instead just picking stuff I think is useful. This forces me to learn
|
||||||
|
only as many bindings as I find necessary; no more, no less.
|
||||||
|
|
||||||
|
I also define prefix leaders for differing applications. These are
|
||||||
|
quite self explanatory by their name and provide a nice way to
|
||||||
|
visualise all bindings under a specific heading just by searching the
|
||||||
|
code.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package general
|
||||||
|
:straight t
|
||||||
|
:demand t
|
||||||
|
:config
|
||||||
|
;; General which key definitions for leaders
|
||||||
|
(general-def
|
||||||
|
:states '(normal motion)
|
||||||
|
"SPC" 'nil
|
||||||
|
"\\" '(nil :which-key "Local leader")
|
||||||
|
"SPC c" '(nil :which-key "Code")
|
||||||
|
"SPC f" '(nil :which-key "File")
|
||||||
|
"SPC t" '(nil :which-key "Shell")
|
||||||
|
"SPC m" '(nil :which-key "Toggle modes")
|
||||||
|
"SPC a" '(nil :which-key "Applications")
|
||||||
|
"SPC s" '(nil :which-key "Search")
|
||||||
|
"SPC b" '(nil :which-key "Buffers")
|
||||||
|
"SPC q" '(nil :which-key "Quit/Literate")
|
||||||
|
"SPC i" '(nil :which-key "Insert")
|
||||||
|
"SPC d" '(nil :which-key "Directories"))
|
||||||
|
|
||||||
|
(general-create-definer leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:keymaps 'override
|
||||||
|
:prefix "SPC")
|
||||||
|
|
||||||
|
(general-create-definer local-leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:prefix "\\")
|
||||||
|
|
||||||
|
(general-create-definer code-leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:keymaps 'override
|
||||||
|
:prefix "SPC c")
|
||||||
|
|
||||||
|
(general-create-definer file-leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:keymaps 'override
|
||||||
|
:prefix "SPC f")
|
||||||
|
|
||||||
|
(general-create-definer shell-leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:keymaps 'override
|
||||||
|
:prefix "SPC t")
|
||||||
|
|
||||||
|
(general-create-definer mode-leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:keymaps 'override
|
||||||
|
:prefix "SPC m")
|
||||||
|
|
||||||
|
(general-create-definer app-leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:keymaps 'override
|
||||||
|
:prefix "SPC a")
|
||||||
|
|
||||||
|
(general-create-definer search-leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:keymaps 'override
|
||||||
|
:prefix "SPC s")
|
||||||
|
|
||||||
|
(general-create-definer buffer-leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:keymaps 'override
|
||||||
|
:prefix "SPC b")
|
||||||
|
|
||||||
|
(general-create-definer quit-leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:keymaps 'override
|
||||||
|
:prefix "SPC q")
|
||||||
|
|
||||||
|
(general-create-definer insert-leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:keymaps 'override
|
||||||
|
:prefix "SPC i")
|
||||||
|
|
||||||
|
(general-create-definer dir-leader
|
||||||
|
:states '(normal motion)
|
||||||
|
:keymaps 'override
|
||||||
|
:prefix "SPC d")
|
||||||
|
|
||||||
|
(general-create-definer general-nmmap
|
||||||
|
:states '(normal motion))
|
||||||
|
|
||||||
|
(defalias 'nmmap #'general-nmmap)
|
||||||
|
|
||||||
|
(general-evil-setup t))
|
||||||
|
#+end_src
|
||||||
|
** Some binds in Emacs
|
||||||
|
Some bindings that I couldn't fit elsewhere easily.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package emacs
|
||||||
|
:straight nil
|
||||||
|
:general
|
||||||
|
(general-def
|
||||||
|
"C-x d" #'delete-frame)
|
||||||
|
|
||||||
|
(nmmap
|
||||||
|
"C--" #'text-scale-decrease
|
||||||
|
"C-=" #'text-scale-increase)
|
||||||
|
|
||||||
|
(leader
|
||||||
|
"SPC" '(execute-extended-command :which-key "M-x")
|
||||||
|
"'" '(browse-url-emacs :which-key "Open url in Emacs")
|
||||||
|
";" 'eval-expression
|
||||||
|
":" `(,(proc (interactive) (switch-to-buffer "*scratch*"))
|
||||||
|
:which-key "Switch to *scratch*")
|
||||||
|
"!" '(async-shell-command :which-key "Async shell command")
|
||||||
|
"h" '(help-command :which-key "Help"))
|
||||||
|
|
||||||
|
(mode-leader
|
||||||
|
"t" #'+oreo/switch-theme)
|
||||||
|
|
||||||
|
(code-leader
|
||||||
|
"F" `(,(proc (interactive) (find-file "~/Code/"))
|
||||||
|
:which-key "Open ~/Code/"))
|
||||||
|
|
||||||
|
(file-leader
|
||||||
|
"f" #'find-file
|
||||||
|
"F" #'find-file-other-frame
|
||||||
|
"s" #'save-buffer
|
||||||
|
"p" `(,(proc (interactive)
|
||||||
|
(find-file (concat user-emacs-directory "config.org")))
|
||||||
|
:which-key "Open config.org"))
|
||||||
|
|
||||||
|
(quit-leader
|
||||||
|
"q" #'save-buffers-kill-terminal
|
||||||
|
"c" #'+literate/compile-config
|
||||||
|
"l" #'+literate/load-config
|
||||||
|
"d" #'delete-frame)
|
||||||
|
|
||||||
|
(search-leader "i" #'imenu))
|
||||||
|
#+end_src
|
||||||
|
* Evil
|
||||||
|
My editor journey started off with Vim rather than Emacs, so my brain
|
||||||
|
has imprinted on its style. Thankfully Emacs is super extensible so
|
||||||
|
there exists a package (more of a supreme system) for porting Vim's
|
||||||
|
modal editing style to Emacs, called Evil (Emacs Vi Layer).
|
||||||
|
|
||||||
|
However there are a lot of packages in Vim that provide greater
|
||||||
|
functionality, for example 'vim-surround'. Emacs, by default, has
|
||||||
|
these capabilities but there are further packages which integrate them
|
||||||
|
into Evil.
|
||||||
|
** Evil core
|
||||||
|
Setup the evil package, with some opinionated keybindings:
|
||||||
|
- Switch ~evil-upcase~ and ~evil-downcase~ because I use ~evil-upcase~
|
||||||
|
more
|
||||||
|
- Switch ~evil-goto-mark~ and ~evil-goto-mark-line~ as I'd rather have
|
||||||
|
the global one closer to the home row
|
||||||
|
- Use 'T' character as an action for transposing objects
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package evil
|
||||||
|
:demand t
|
||||||
|
:hook (after-init-hook . evil-mode)
|
||||||
|
:general
|
||||||
|
(leader
|
||||||
|
"w" '(evil-window-map :which-key "Window")
|
||||||
|
"wd" #'delete-frame)
|
||||||
|
|
||||||
|
(nmmap
|
||||||
|
"TAB" #'evil-jump-item
|
||||||
|
"r" #'evil-replace-state
|
||||||
|
"zC" #'hs-hide-level
|
||||||
|
"zO" #'hs-show-all
|
||||||
|
"'" #'evil-goto-mark
|
||||||
|
"`" #'evil-goto-mark-line
|
||||||
|
"C-w" #'evil-window-map
|
||||||
|
"gu" #'evil-upcase
|
||||||
|
"gU" #'evil-downcase
|
||||||
|
"T" nil)
|
||||||
|
|
||||||
|
(nmmap
|
||||||
|
:infix "T"
|
||||||
|
"w" #'transpose-words
|
||||||
|
"c" #'transpose-chars
|
||||||
|
"s" #'transpose-sentences
|
||||||
|
"p" #'transpose-paragraphs
|
||||||
|
"e" #'transpose-sexps
|
||||||
|
"l" #'transpose-lines)
|
||||||
|
:init
|
||||||
|
(setq evil-want-keybinding nil
|
||||||
|
evil-split-window-below t
|
||||||
|
evil-vsplit-window-right t
|
||||||
|
evil-want-abbrev-expand-on-insert-exit t
|
||||||
|
evil-undo-system #'undo-tree)
|
||||||
|
:config
|
||||||
|
(fset #'evil-window-vsplit #'make-frame))
|
||||||
|
#+end_src
|
||||||
|
** Evil surround
|
||||||
|
Evil surround is a port for vim-surround.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package evil-surround
|
||||||
|
:after evil
|
||||||
|
:config
|
||||||
|
(global-evil-surround-mode))
|
||||||
|
#+end_src
|
||||||
|
** Evil commentary
|
||||||
|
Allows generalised commenting of objects easily.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package evil-commentary
|
||||||
|
:after evil
|
||||||
|
:config
|
||||||
|
(evil-commentary-mode))
|
||||||
|
#+end_src
|
||||||
|
** Evil multi cursor
|
||||||
|
Setup for multi cursors in Evil mode. Don't let evil-mc setup it's own
|
||||||
|
keymap because it uses 'gr' as its prefix, which I don't like.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package evil-mc
|
||||||
|
:after evil
|
||||||
|
:init
|
||||||
|
(defvar evil-mc-key-map (make-sparse-keymap))
|
||||||
|
:general
|
||||||
|
(nmap
|
||||||
|
:infix "gz"
|
||||||
|
"q" #'evil-mc-undo-all-cursors
|
||||||
|
"d" #'evil-mc-make-and-goto-next-match
|
||||||
|
"j" #'evil-mc-make-cursor-move-next-line
|
||||||
|
"k" #'evil-mc-make-cursor-move-prev-line
|
||||||
|
"j" #'evil-mc-make-cursor-move-next-line
|
||||||
|
"m" #'evil-mc-make-all-cursors
|
||||||
|
"z" #'evil-mc-make-cursor-here
|
||||||
|
"r" #'evil-mc-resume-cursors
|
||||||
|
"s" #'evil-mc-pause-cursors
|
||||||
|
"u" #'evil-mc-undo-last-added-cursor)
|
||||||
|
:config
|
||||||
|
;; (evil-mc-define-vars)
|
||||||
|
;; (evil-mc-initialize-vars)
|
||||||
|
;; (add-hook 'evil-mc-before-cursors-created #'evil-mc-pause-incompatible-modes)
|
||||||
|
;; (add-hook 'evil-mc-before-cursors-created #'evil-mc-initialize-active-state)
|
||||||
|
;; (add-hook 'evil-mc-after-cursors-deleted #'evil-mc-teardown-active-state)
|
||||||
|
;; (add-hook 'evil-mc-after-cursors-deleted #'evil-mc-resume-incompatible-modes)
|
||||||
|
;; (advice-add #'evil-mc-initialize-hooks :override #'ignore)
|
||||||
|
;; (advice-add #'evil-mc-teardown-hooks :override #'evil-mc-initialize-vars)
|
||||||
|
;; (advice-add #'evil-mc-initialize-active-state :before #'turn-on-evil-mc-mode)
|
||||||
|
;; (advice-add #'evil-mc-teardown-active-state :after #'turn-off-evil-mc-mode)
|
||||||
|
;; (add-hook 'evil-insert-state-entry-hook #'evil-mc-resume-cursors)
|
||||||
|
(global-evil-mc-mode))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
** Evil collection
|
||||||
|
Provides a community based set of keybindings for most modes in
|
||||||
|
Emacs. I don't necessarily like all my modes having these bindings
|
||||||
|
though, as I may disagree with some. So I use it in a mode to mode basis.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package evil-collection
|
||||||
|
:after evil)
|
||||||
|
#+end_src
|
||||||
|
* Completion
|
||||||
|
Emacs is a text based interface. Completion is its bread and butter
|
||||||
|
in providing good user experience. By default Emacs provides
|
||||||
|
'completions-list' which produces a buffer of options which can be
|
||||||
|
searched and selected. We can take this further though!
|
||||||
|
|
||||||
|
Ido and Icomplete are packages distributed with Emacs to provide
|
||||||
|
greater completion interfaces. They utilise the minibuffer to create
|
||||||
|
a more interactive experience, allowing incremental searches and
|
||||||
|
option selection.
|
||||||
|
|
||||||
|
Ivy and Helm provide more modern interfaces, though Helm is quite
|
||||||
|
heavy. Ivy, on the other hand, provides an interface similar to Ido
|
||||||
|
with less clutter and better customisation options.
|
||||||
|
** Ivy
|
||||||
|
Ivy is a completion framework for Emacs, and my preferred one. It has
|
||||||
|
a great set of features with little to no pain with setting up.
|
||||||
|
*** Ivy Core
|
||||||
|
Setup for ivy, in preparation for counsel. Turn on ivy-mode just
|
||||||
|
after init.
|
||||||
|
|
||||||
|
Setup vim-like bindings for the minibuffer ("M-(j|k)" for down|up the
|
||||||
|
selection list).
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package ivy
|
||||||
|
:defer t
|
||||||
|
:hook (after-init-hook . ivy-mode)
|
||||||
|
:general
|
||||||
|
(general-def
|
||||||
|
:keymaps 'ivy-minibuffer-map
|
||||||
|
"C-j" #'ivy-yank-symbol
|
||||||
|
"M-j" #'ivy-next-line-or-history
|
||||||
|
"M-k" #'ivy-previous-line-or-history
|
||||||
|
"C-SPC" #'ivy-occur)
|
||||||
|
(general-def
|
||||||
|
:keymaps 'ivy-switch-buffer-map
|
||||||
|
"M-j" #'ivy-next-line-or-history
|
||||||
|
"M-k" #'ivy-previous-line-or-history)
|
||||||
|
(nmap
|
||||||
|
:keymaps '(ivy-occur-mode-map ivy-occur-grep-mode-map)
|
||||||
|
"RET" #'ivy-occur-press-and-switch
|
||||||
|
"J" #'ivy-occur-press
|
||||||
|
"gr" #'ivy-occur-revert-buffer
|
||||||
|
"q" #'quit-window
|
||||||
|
"D" #'ivy-occur-delete-candidate
|
||||||
|
"W" #'ivy-wgrep-change-to-wgrep-mode
|
||||||
|
"{" #'compilation-previous-file
|
||||||
|
"}" #'compilation-next-file)
|
||||||
|
:init
|
||||||
|
(with-eval-after-load "evil"
|
||||||
|
(evil-set-initial-state 'ivy-occur-mode 'normal)
|
||||||
|
(evil-set-initial-state 'ivy-occur-grep-mode 'normal))
|
||||||
|
(with-eval-after-load "amx"
|
||||||
|
(setq amx-backend 'ivy))
|
||||||
|
|
||||||
|
(setq ivy-height 10
|
||||||
|
ivy-wrap t
|
||||||
|
ivy-fixed-height-minibuffer t
|
||||||
|
ivy-use-virtual-buffers nil
|
||||||
|
ivy-virtual-abbreviate 'full
|
||||||
|
ivy-on-del-error-function #'ignore
|
||||||
|
ivy-use-selectable-prompt t)
|
||||||
|
:config
|
||||||
|
(require 'counsel nil t))
|
||||||
|
#+end_src
|
||||||
|
*** Counsel
|
||||||
|
Setup for counsel. Load after ivy and helpful.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package counsel
|
||||||
|
:defer t
|
||||||
|
:general
|
||||||
|
(search-leader
|
||||||
|
"s" #'counsel-grep-or-swiper
|
||||||
|
"r" #'counsel-rg)
|
||||||
|
(file-leader
|
||||||
|
"r" #'counsel-recentf)
|
||||||
|
(insert-leader
|
||||||
|
"c" #'counsel-unicode-char)
|
||||||
|
(general-def
|
||||||
|
[remap describe-bindings] #'counsel-descbinds
|
||||||
|
[remap load-theme] #'counsel-load-theme)
|
||||||
|
:config
|
||||||
|
(setq ivy-initial-inputs-alist '((org-insert-link . "^"))
|
||||||
|
counsel-describe-function-function #'helpful-callable
|
||||||
|
counsel-describe-variable-function #'helpful-variable
|
||||||
|
counsel-grep-swiper-limit 1500000
|
||||||
|
ivy-re-builders-alist '((swiper . ivy--regex-plus)
|
||||||
|
(counsel-grep-or-swiper . ivy--regex-plus)
|
||||||
|
(counsel-rg . ivy--regex-plus)
|
||||||
|
(t . ivy--regex-ignore-order)))
|
||||||
|
(counsel-mode))
|
||||||
|
#+end_src
|
||||||
|
*** WIP Ivy posframe
|
||||||
|
:PROPERTIES:
|
||||||
|
:header-args:emacs-lisp: :tangle no
|
||||||
|
:END:
|
||||||
|
This makes ivy minibuffer windows use child frames.
|
||||||
|
Very nice eyecandy, but can get kinda annoying.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package ivy-posframe
|
||||||
|
:hook (ivy-mode-hook . ivy-posframe-mode)
|
||||||
|
:straight t
|
||||||
|
:init
|
||||||
|
(setq ivy-posframe-parameters
|
||||||
|
'((left-fringe . 0)
|
||||||
|
(right-fringe . 0)
|
||||||
|
(background-color . "grey7")))
|
||||||
|
|
||||||
|
(setq ivy-posframe-display-functions-alist
|
||||||
|
'((t . ivy-posframe-display-at-window-center))))
|
||||||
|
#+end_src
|
||||||
|
*** WIP Counsel etags
|
||||||
|
:PROPERTIES:
|
||||||
|
:header-args:emacs-lisp: :tangle no
|
||||||
|
:END:
|
||||||
|
Counsel etags allows me to search generated tag files for tags. I
|
||||||
|
already have a function defined to generate the tags, so it's just
|
||||||
|
searching them which I find to be a bit of a hassle, and where this
|
||||||
|
package comes in.
|
||||||
|
|
||||||
|
This has been replaced by [[*xref][xref]] which is inbuilt.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package counsel-etags
|
||||||
|
:after counsel
|
||||||
|
:general
|
||||||
|
(search-leader
|
||||||
|
"t" #'counsel-etags-find-tag))
|
||||||
|
#+end_src
|
||||||
|
** WIP Ido
|
||||||
|
:PROPERTIES:
|
||||||
|
:header-args:emacs-lisp: :tangle no
|
||||||
|
:END:
|
||||||
|
Ido is a very old completion package that still works great to this
|
||||||
|
day. Though it is limited in its scope (and may thus be called a
|
||||||
|
completion add-on rather than a full on framework), it is still a very
|
||||||
|
powerful package. With the use of ido-completing-read+, it may be used
|
||||||
|
similarly to a fully fledged completion framework.
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package ido
|
||||||
|
:demand t
|
||||||
|
:general
|
||||||
|
(general-def
|
||||||
|
:keymaps '(ido-buffer-completion-map
|
||||||
|
ido-file-completion-map
|
||||||
|
ido-file-dir-completion-map
|
||||||
|
ido-common-completion-map)
|
||||||
|
(kbd "M-j") #'ido-next-match
|
||||||
|
(kbd "M-k") #'ido-prev-match
|
||||||
|
(kbd "C-x o") #'evil-window-up)
|
||||||
|
:init
|
||||||
|
(setq ido-decorations
|
||||||
|
(list "{" "}" " \n" " ..." "[" "]" " [No match]" " [Matched]"
|
||||||
|
" [Not readable]" " [Too big]" " [Confirm]")
|
||||||
|
completion-styles '(flex partial-completion intials emacs22))
|
||||||
|
(setq-default ido-enable-flex-matching t
|
||||||
|
ido-enable-dot-prefix t
|
||||||
|
ido-enable-regexp nil)
|
||||||
|
(with-eval-after-load "magit"
|
||||||
|
(setq magit-completing-read-function 'magit-ido-completing-read))
|
||||||
|
:config
|
||||||
|
(ido-mode)
|
||||||
|
(ido-everywhere))
|
||||||
|
#+end_src
|
||||||
|
*** Ido ubiquitous
|
||||||
|
Ido completing-read+ is a package that extends the ido package to work
|
||||||
|
with more text based functions.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package ido-completing-read+
|
||||||
|
:after ido
|
||||||
|
:config
|
||||||
|
(ido-ubiquitous-mode +1))
|
||||||
|
#+end_src
|
||||||
|
** Amx
|
||||||
|
Amx is a fork of Smex that works to enhance the
|
||||||
|
execute-extended-command interface. It also provides support for ido
|
||||||
|
or ivy (though I'm likely to use ido here) and allows you to switch
|
||||||
|
between them.
|
||||||
|
|
||||||
|
It provides a lot of niceties such as presenting the key bind when
|
||||||
|
looking for a command.
|
||||||
|
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package amx
|
||||||
|
:config
|
||||||
|
(amx-mode))
|
||||||
|
#+end_src
|
||||||
|
** Orderless
|
||||||
|
Orderless sorting method for completion, probably one of the best
|
||||||
|
things ever.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package orderless
|
||||||
|
:after (ivy ido)
|
||||||
|
:config
|
||||||
|
(setf (alist-get t ivy-re-builders-alist) 'orderless-ivy-re-builder))
|
||||||
|
#+end_src
|
||||||
|
** Completions-list
|
||||||
|
In case I ever use the completions list, some basic commands to look
|
||||||
|
around.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package simple
|
||||||
|
:straight nil
|
||||||
|
:general
|
||||||
|
(nmmap
|
||||||
|
:keymaps 'completion-list-mode-map
|
||||||
|
"l" #'next-completion
|
||||||
|
"h" #'previous-completion
|
||||||
|
"ESC" #'delete-completion-window
|
||||||
|
"q" #'quit-window
|
||||||
|
"RET" #'choose-completion)
|
||||||
|
:config
|
||||||
|
(with-eval-after-load "evil"
|
||||||
|
(setq evil-emacs-state-modes (cl-remove-if
|
||||||
|
#'(lambda (x) (eq 'completions-list-mode x))
|
||||||
|
evil-emacs-state-modes))
|
||||||
|
(add-to-list 'evil-normal-state-modes 'completions-list-mode)))
|
||||||
|
#+end_src
|
||||||
|
** Company
|
||||||
|
Company is the auto complete system I use. I don't like having heavy
|
||||||
|
setups for company as it only makes it slower to use. In this case,
|
||||||
|
just setup some evil binds for company.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package company
|
||||||
|
:straight t
|
||||||
|
:hook
|
||||||
|
(prog-mode-hook . company-mode)
|
||||||
|
(eshell-mode-hook . company-mode)
|
||||||
|
:general
|
||||||
|
(imap
|
||||||
|
"C-SPC" #'company-complete)
|
||||||
|
(general-def
|
||||||
|
:states '(normal insert)
|
||||||
|
"M-j" #'company-select-next
|
||||||
|
"M-k" #'company-select-previous))
|
||||||
|
#+end_src
|
||||||
|
* Pretty symbols
|
||||||
|
Prettify symbols mode allows for users to declare 'symbols' that
|
||||||
|
replace text within certain modes. Though this may seem like useless
|
||||||
|
eye candy, it has aided my comprehension and speed of recognition
|
||||||
|
(recognising symbols is easier than words).
|
||||||
|
|
||||||
|
Essentially a use-package keyword which makes declaring pretty symbols
|
||||||
|
for language modes incredibly easy. Checkout my [[C/C++][C/C++]] configuration
|
||||||
|
for an example.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package prog-mode
|
||||||
|
:straight nil
|
||||||
|
:init
|
||||||
|
(setq prettify-symbols-unprettify-at-point t)
|
||||||
|
:config
|
||||||
|
(with-eval-after-load "use-package-core"
|
||||||
|
(add-to-list 'use-package-keywords ':pretty)
|
||||||
|
(defun use-package-normalize/:pretty (_name-symbol _keyword args)
|
||||||
|
args)
|
||||||
|
|
||||||
|
(defun use-package-handler/:pretty (name _keyword args rest state)
|
||||||
|
(use-package-concat
|
||||||
|
(use-package-process-keywords name rest state)
|
||||||
|
(mapcar
|
||||||
|
#'(lambda (arg)
|
||||||
|
(let ((mode (car arg))
|
||||||
|
(rest (cdr arg)))
|
||||||
|
`(add-hook
|
||||||
|
',mode
|
||||||
|
#'(lambda nil
|
||||||
|
(setq prettify-symbols-alist ',rest)
|
||||||
|
(prettify-symbols-mode)))))
|
||||||
|
args)))))
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
Here's a collection of keywords and possible associated symbols for
|
||||||
|
any prog language of choice. Mostly for reference and copying.
|
||||||
|
#+begin_example
|
||||||
|
("null" . "Ø")
|
||||||
|
("list" . "ℓ")
|
||||||
|
("string" . "𝕊")
|
||||||
|
("true" . "⊤")
|
||||||
|
("false" . "⊥")
|
||||||
|
("char" . "ℂ")
|
||||||
|
("int" . "ℤ")
|
||||||
|
("float" . "ℝ")
|
||||||
|
("!" . "¬")
|
||||||
|
("&&" . "∧")
|
||||||
|
("||" . "∨")
|
||||||
|
("for" . "∀")
|
||||||
|
("return" . "⟼")
|
||||||
|
("print" . "ℙ")
|
||||||
|
("lambda" . "λ")
|
||||||
|
#+end_example
|
||||||
|
* Window management
|
||||||
|
Emacs' default window management is quite bad, eating other windows on
|
||||||
|
a whim and not particularly caring for the current window setup.
|
||||||
|
Thankfully you can change this via the ~display-buffer-alist~ which
|
||||||
|
matches buffer names with how the window for the buffer should be
|
||||||
|
displayed. I add a use-package keyword to make ~display-buffer-alist~
|
||||||
|
records within use-package.
|
||||||
|
|
||||||
|
I have no idea whether it's optimal AT ALL, but it works for me.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package window
|
||||||
|
:straight nil
|
||||||
|
:general
|
||||||
|
(buffer-leader
|
||||||
|
"b" #'switch-to-buffer
|
||||||
|
"d" #'kill-current-buffer
|
||||||
|
"K" #'kill-buffer
|
||||||
|
"j" #'next-buffer
|
||||||
|
"k" #'previous-buffer
|
||||||
|
"D" '(+oreo/clean-buffer-list :which-key "Kill most buffers"))
|
||||||
|
:init
|
||||||
|
(with-eval-after-load "use-package-core"
|
||||||
|
(add-to-list 'use-package-keywords ':display)
|
||||||
|
(defun use-package-normalize/:display (_name-symbol _keyword args)
|
||||||
|
args)
|
||||||
|
|
||||||
|
(defun use-package-handler/:display (name _keyword args rest state)
|
||||||
|
(use-package-concat
|
||||||
|
(use-package-process-keywords name rest state)
|
||||||
|
(mapcar
|
||||||
|
#'(lambda (arg)
|
||||||
|
`(add-to-list 'display-buffer-alist
|
||||||
|
',arg))
|
||||||
|
args)))))
|
||||||
|
#+end_src
|
||||||
|
** Some display records
|
||||||
|
Using the ~:display~ keyword, setup up some ~display-buffer-alist~
|
||||||
|
records. This is mostly for packages that aren't really configured
|
||||||
|
(like [[info:woman][woman]]) or packages that were configured before
|
||||||
|
(like [[Ivy][Ivy]]).
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package window
|
||||||
|
:straight nil
|
||||||
|
:defer t
|
||||||
|
:display
|
||||||
|
("\\*Process List\\*"
|
||||||
|
(display-buffer-at-bottom)
|
||||||
|
(window-height . 0.25))
|
||||||
|
|
||||||
|
("\\*\\(Ido \\)?Completions\\*"
|
||||||
|
(display-buffer-in-side-window)
|
||||||
|
(window-height . 0.25)
|
||||||
|
(side . bottom))
|
||||||
|
|
||||||
|
("\\*ivy-occur.*"
|
||||||
|
(display-buffer-at-bottom)
|
||||||
|
(window-height . 0.25))
|
||||||
|
|
||||||
|
("\\*Async Shell Command\\*"
|
||||||
|
(display-buffer-at-bottom)
|
||||||
|
(window-height . 0.25)))
|
||||||
|
#+end_src
|
||||||
|
* Auto typing
|
||||||
|
Snippets are a pretty nice way of automatically inserting code. Emacs
|
||||||
|
provides a ton of packages by default to do this, but there are great
|
||||||
|
packages to install as well.
|
||||||
|
|
||||||
|
Abbrevs and skeletons make up a popular solution within Emacs default.
|
||||||
|
Abbrevs are for simple expressions wherein the only input is the key,
|
||||||
|
and the output is some Elisp function. They provide a lot of inbuilt
|
||||||
|
functionality and are quite useful. Skeletons, on the other hand, are
|
||||||
|
for higher level insertions
|
||||||
|
|
||||||
|
The popular external solution is Yasnippet. Yasnippet is a great
|
||||||
|
package for snippets, which I use heavily in programming and org-mode.
|
||||||
|
I setup here the global mode for yasnippet and a collection of
|
||||||
|
snippets for ease of use.
|
||||||
|
** Abbrevs
|
||||||
|
Just define a few abbrevs for various date-time operations. Also
|
||||||
|
define a macro that will assume a function for the expansion, helping
|
||||||
|
with abstracting a few things away.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package abbrev
|
||||||
|
:straight nil
|
||||||
|
:hook
|
||||||
|
(prog-mode-hook . abbrev-mode)
|
||||||
|
(text-mode-hook . abbrev-mode)
|
||||||
|
:init
|
||||||
|
(defmacro +abbrev/define-abbrevs (abbrev-table &rest abbrevs)
|
||||||
|
`(progn
|
||||||
|
,@(mapcar #'(lambda (abbrev)
|
||||||
|
`(define-abbrev
|
||||||
|
,abbrev-table
|
||||||
|
,(car abbrev)
|
||||||
|
""
|
||||||
|
(proc (insert ,(cadr abbrev)))))
|
||||||
|
abbrevs)))
|
||||||
|
(setq save-abbrevs nil)
|
||||||
|
:config
|
||||||
|
(+abbrev/define-abbrevs
|
||||||
|
global-abbrev-table
|
||||||
|
("sdate"
|
||||||
|
(format-time-string "%Y-%m-%d" (current-time)))
|
||||||
|
("stime"
|
||||||
|
(format-time-string "%H:%M:%S" (current-time)))
|
||||||
|
("sday"
|
||||||
|
(format-time-string "%A" (current-time)))
|
||||||
|
("smon"
|
||||||
|
(format-time-string "%B" (current-time)))))
|
||||||
|
#+end_src
|
||||||
|
** WIP Skeletons
|
||||||
|
:PROPERTIES:
|
||||||
|
:header-args:emacs-lisp: :tangle no
|
||||||
|
:END:
|
||||||
|
Defines a macro for generating a skeleton + abbrev for a given mode.
|
||||||
|
Doesn't sanitise inputs because I assume callers are /rational/ actors
|
||||||
|
who would *only* use this for their top level Emacs config.
|
||||||
|
|
||||||
|
Honestly didn't find much use for this currently, so disabled.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package skeleton
|
||||||
|
:straight nil
|
||||||
|
:after abbrev
|
||||||
|
:config
|
||||||
|
(defmacro +autotyping/gen-skeleton-abbrev (mode abbrev &rest skeleton)
|
||||||
|
(let* ((table (intern (concat (symbol-name mode) "-abbrev-table")))
|
||||||
|
(skeleton-name (intern (concat "+skeleton/" (symbol-name mode) "/" abbrev))))
|
||||||
|
`(progn
|
||||||
|
(define-skeleton
|
||||||
|
,skeleton-name
|
||||||
|
""
|
||||||
|
,@skeleton)
|
||||||
|
(define-abbrev ,table
|
||||||
|
,abbrev
|
||||||
|
""
|
||||||
|
',skeleton-name)))))
|
||||||
|
#+end_src
|
||||||
|
** Auto insert
|
||||||
|
Allows inserting text immediately upon creating a new buffer with a
|
||||||
|
given name. Supports skeletons for inserting text. To make it easier
|
||||||
|
for later systems to define their own auto inserts, I define a
|
||||||
|
~use-package~ keyword ~auto-insert~ which allows one to define an
|
||||||
|
entry for ~auto-insert-alist~.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package autoinsert
|
||||||
|
:straight nil
|
||||||
|
:demand t
|
||||||
|
:hook (after-init-hook . auto-insert-mode)
|
||||||
|
:config
|
||||||
|
(with-eval-after-load "use-package-core"
|
||||||
|
(add-to-list 'use-package-keywords ':auto-insert)
|
||||||
|
(defun use-package-normalize/:auto-insert (_name-symbol _keyword args)
|
||||||
|
args)
|
||||||
|
(defun use-package-handler/:auto-insert (name _keyword args rest state)
|
||||||
|
(use-package-concat
|
||||||
|
(use-package-process-keywords name rest state)
|
||||||
|
(mapcar
|
||||||
|
#'(lambda (arg)
|
||||||
|
`(add-to-list
|
||||||
|
'auto-insert-alist
|
||||||
|
',arg))
|
||||||
|
args)))))
|
||||||
|
#+end_src
|
||||||
|
** Yasnippet default
|
||||||
|
Look at the snippets [[file:../.config/yasnippet/snippets/][folder]]
|
||||||
|
for all snippets I've got.
|
||||||
|
#+begin_src emacs-lisp
|
||||||
|
(use-package yasnippet
|
||||||
|
:defer t
|
||||||
|
:hook
|
||||||
|
(prog-mode-hook . yas-minor-mode)
|
||||||
|
:general
|
||||||
|
(insert-leader
|
||||||
|
"i" #'yas-insert-snippet)
|
||||||
|
:config
|
||||||
|
(yas-load-directory (no-littering-expand-etc-file-name "yasnippet/snippets")))
|
||||||
|
#+end_src
|
||||||
@@ -33,7 +33,17 @@
|
|||||||
(cons first (+literate/filter predicate rest))
|
(cons first (+literate/filter predicate rest))
|
||||||
(+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
|
(defconst +literate/output-files
|
||||||
(mapcar #'(lambda (x) (replace-regexp-in-string ".org" ".el" x)) +literate/org-files))
|
(mapcar #'(lambda (x) (replace-regexp-in-string ".org" ".el" x)) +literate/org-files))
|
||||||
@@ -41,11 +51,12 @@
|
|||||||
(defconst +literate/elisp-files
|
(defconst +literate/elisp-files
|
||||||
`(,(concat user-emacs-directory "early-init.el")
|
`(,(concat user-emacs-directory "early-init.el")
|
||||||
,(concat user-emacs-directory "init.el")
|
,(concat user-emacs-directory "init.el")
|
||||||
|
,@+literate/output-files
|
||||||
,@(mapcar
|
,@(mapcar
|
||||||
#'(lambda (name) (concat user-emacs-directory "elisp/" name))
|
#'(lambda (name) (concat user-emacs-directory "elisp/" name))
|
||||||
;; Only take .el files
|
;; Only take .el files
|
||||||
(+literate/filter
|
(+literate/filter
|
||||||
(lambda (name) (string= "el" (file-name-extension name)))
|
#'+literate/el-p
|
||||||
(cddr (directory-files (concat user-emacs-directory "elisp/")))))))
|
(cddr (directory-files (concat user-emacs-directory "elisp/")))))))
|
||||||
|
|
||||||
;; Setup predicates and loading
|
;; Setup predicates and loading
|
||||||
@@ -62,14 +73,15 @@
|
|||||||
(file-exists-p (car +literate/output-files))))
|
(file-exists-p (car +literate/output-files))))
|
||||||
|
|
||||||
(defun +literate/load-config ()
|
(defun +literate/load-config ()
|
||||||
"Load all files in +literate/output-files."
|
"Load the first file in +literate/output-files."
|
||||||
(interactive)
|
(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")
|
(autoload #'org-babel-tangle-file "ob-tangle")
|
||||||
|
|
||||||
(defun +literate/tangle-if-old (org-file)
|
(defun +literate/tangle-if-old (org-file)
|
||||||
(let ((output-file (replace-regexp-in-string ".org" ".el" 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))
|
(if (or (not (file-exists-p output-file)) (file-newer-than-file-p org-file output-file))
|
||||||
(org-babel-tangle-file org-file))))
|
(org-babel-tangle-file org-file))))
|
||||||
|
|
||||||
@@ -88,7 +100,7 @@
|
|||||||
(message "Byte-compiling literate files...")
|
(message "Byte-compiling literate files...")
|
||||||
(mapc #'+literate/byte-compile-if-old +literate/output-files)
|
(mapc #'+literate/byte-compile-if-old +literate/output-files)
|
||||||
(message "Literate files byte-compiled")
|
(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)
|
(mapc #'+literate/byte-compile-if-old +literate/elisp-files)
|
||||||
(message "Finished byte-compiling"))
|
(message "Finished byte-compiling"))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user