(Emacs)~Merged app.org, lang.org and core.org back into config.org
Just makes it easier to profile and deal with compilation
This commit is contained in:
@@ -1,907 +0,0 @@
|
|||||||
#+title: Applications for Emacs
|
|
||||||
#+author: Aryadev Chavali
|
|
||||||
#+description: Applications for my Emacs OS™
|
|
||||||
#+date: 2023-09-29
|
|
||||||
#+property: header-args:emacs-lisp :tangle app.el :comments link :results none
|
|
||||||
#+options: toc:nil
|
|
||||||
#+startup: noindent
|
|
||||||
|
|
||||||
Applications are greater than packages; they provide a set of
|
|
||||||
functionality to create an interface in Emacs. Emacs comes with
|
|
||||||
applications and others may be installed.
|
|
||||||
|
|
||||||
* WAIT Dashboard
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
Dashboard creates a custom dashboard for Emacs that replaces the
|
|
||||||
initial startup screen in default Emacs. It has a lot of customising
|
|
||||||
options.
|
|
||||||
|
|
||||||
Unfortunately not that useful, many things are easier to invoke
|
|
||||||
directly such as recent files or project changing.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package dashboard
|
|
||||||
:straight t
|
|
||||||
:demand t
|
|
||||||
:general
|
|
||||||
(app-leader
|
|
||||||
"b" #'dashboard-refresh-buffer)
|
|
||||||
(:states '(normal motion emacs)
|
|
||||||
:keymaps 'dashboard-mode-map
|
|
||||||
"q" (proc (interactive) (kill-this-buffer)))
|
|
||||||
(nmmap
|
|
||||||
:keymaps 'dashboard-mode-map
|
|
||||||
"r" #'dashboard-jump-to-recent-files
|
|
||||||
"p" #'dashboard-jump-to-projects
|
|
||||||
"}" #'dashboard-next-section
|
|
||||||
"{" #'dashboard-previous-section)
|
|
||||||
:init
|
|
||||||
(setq initial-buffer-choice nil
|
|
||||||
dashboard-banner-logo-title "Oreomacs"
|
|
||||||
dashboard-center-content t
|
|
||||||
dashboard-set-init-info t
|
|
||||||
dashboard-startup-banner (no-littering-expand-etc-file-name "dashboard/logo.png")
|
|
||||||
dashboard-set-footer t
|
|
||||||
dashboard-set-navigator t
|
|
||||||
dashboard-items '((projects . 5)
|
|
||||||
(recents . 5))
|
|
||||||
dashboard-footer-messages (list
|
|
||||||
"Collecting parentheses..."
|
|
||||||
"Linking 'coffee_machine.o'..."
|
|
||||||
"Uploading ip to hacker named 4chan..."
|
|
||||||
"Dividing by zero..."
|
|
||||||
"Solving 3-sat..."
|
|
||||||
"Obtaining your health record..."
|
|
||||||
(format "Recompiling Emacs for the %dth time..." (random 1000))
|
|
||||||
"Escaping the cycle of samsara..."))
|
|
||||||
:config
|
|
||||||
(dashboard-setup-startup-hook))
|
|
||||||
#+end_src
|
|
||||||
* EWW
|
|
||||||
Emacs Web Wowser is the inbuilt text based web browser for Emacs. It
|
|
||||||
can render images and basic CSS styles but doesn't have a JavaScript
|
|
||||||
engine, which makes sense as it's primarily a text interface.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package eww
|
|
||||||
:defer t
|
|
||||||
:general
|
|
||||||
(app-leader
|
|
||||||
"ww" #'eww
|
|
||||||
"wb" #'+eww/bookmarks-search
|
|
||||||
"we" #'+eww/bookmarks-edit)
|
|
||||||
(nmmap
|
|
||||||
:keymaps 'eww-mode-map
|
|
||||||
"w" #'evil-forward-word-begin
|
|
||||||
"Y" #'shr-probe-and-copy-url)
|
|
||||||
:straight nil
|
|
||||||
:config
|
|
||||||
(with-eval-after-load "evil-collection"
|
|
||||||
(evil-collection-eww-setup))
|
|
||||||
(defun bookmark->alist (bookmark)
|
|
||||||
(cons (plist-get bookmark :title)
|
|
||||||
(plist-get bookmark :url)))
|
|
||||||
(defun +eww/bookmarks-edit nil
|
|
||||||
(interactive)
|
|
||||||
(find-file (concat eww-bookmarks-directory "eww-bookmarks")))
|
|
||||||
(defun +eww/bookmarks-search nil
|
|
||||||
(interactive)
|
|
||||||
(let ((bookmarks (mapcar #'bookmark->alist eww-bookmarks)))
|
|
||||||
(eww
|
|
||||||
(alist-get (completing-read "Bookmark: " (mapcar #'car bookmarks) nil t)
|
|
||||||
bookmarks
|
|
||||||
nil
|
|
||||||
nil
|
|
||||||
#'string=)))))
|
|
||||||
#+end_src
|
|
||||||
* Calendar
|
|
||||||
Calendar is a simple inbuilt application that helps with date
|
|
||||||
functionalities. I add functionality to copy dates from the calendar
|
|
||||||
to the kill ring and bind it to "Y".
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package calendar
|
|
||||||
:straight nil
|
|
||||||
:defer t
|
|
||||||
:commands (+calendar/copy-date +calendar/toggle-calendar)
|
|
||||||
:display
|
|
||||||
("\\*Calendar\\*"
|
|
||||||
(display-buffer-at-bottom)
|
|
||||||
(inhibit-duplicate-buffer . t)
|
|
||||||
(window-height . 0.17))
|
|
||||||
:general
|
|
||||||
(nmmap
|
|
||||||
:keymaps 'calendar-mode-map
|
|
||||||
"Y" #'+calendar/copy-date)
|
|
||||||
(app-leader
|
|
||||||
"d" #'calendar)
|
|
||||||
:config
|
|
||||||
(defun +calendar/copy-date ()
|
|
||||||
"Copy date under cursor into kill ring."
|
|
||||||
(interactive)
|
|
||||||
(if (use-region-p)
|
|
||||||
(call-interactively #'kill-ring-save)
|
|
||||||
(let ((date (calendar-cursor-to-date)))
|
|
||||||
(when date
|
|
||||||
(setq date (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))
|
|
||||||
(kill-new (format-time-string "%Y-%m-%d" date)))))))
|
|
||||||
#+end_src
|
|
||||||
* Mail
|
|
||||||
Mail is a funny thing; most people use it just for business or
|
|
||||||
advertising and it's come out of use in terms of personal
|
|
||||||
communication in the west for the most part (largely due to "social"
|
|
||||||
media applications). However, this isn't true for the open source and
|
|
||||||
free software movement who heavily use mail for communication.
|
|
||||||
|
|
||||||
Integrating mail into Emacs helps as I can send source code and
|
|
||||||
integrate it into my workflow just a bit better.
|
|
||||||
** Notmuch
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package notmuch
|
|
||||||
:defer t
|
|
||||||
:commands (notmuch +mail/flag-thread)
|
|
||||||
:general
|
|
||||||
(app-leader "m" #'notmuch)
|
|
||||||
(nmap
|
|
||||||
:keymaps 'notmuch-search-mode-map
|
|
||||||
"f" #'+mail/flag-thread)
|
|
||||||
:init
|
|
||||||
(defconst +mail/signature "---------------\nAryadev Chavali")
|
|
||||||
(defconst +mail/local-dir (no-littering-expand-var-file-name "mail/"))
|
|
||||||
(setq notmuch-show-logo nil
|
|
||||||
notmuch-search-oldest-first nil
|
|
||||||
notmuch-hello-sections '(notmuch-hello-insert-saved-searches
|
|
||||||
notmuch-hello-insert-alltags
|
|
||||||
notmuch-hello-insert-recent-searches)
|
|
||||||
notmuch-archive-tags '("-inbox" "-unread" "+archive")
|
|
||||||
mail-signature +mail/signature
|
|
||||||
mail-default-directory +mail/local-dir
|
|
||||||
mail-source-directory +mail/local-dir
|
|
||||||
message-signature +mail/signature
|
|
||||||
message-auto-save-directory +mail/local-dir
|
|
||||||
message-directory +mail/local-dir)
|
|
||||||
|
|
||||||
(defun +mail/sync-mail ()
|
|
||||||
"Sync mail via mbsync."
|
|
||||||
(interactive)
|
|
||||||
(start-process-shell-command "" nil "mbsync -a"))
|
|
||||||
(defun +mail/trash-junk ()
|
|
||||||
"Delete any mail in junk"
|
|
||||||
(interactive)
|
|
||||||
(start-process-shell-command "" nil "notmuch search --output=files --format=text0 tag:deleted tag:spam tag:trash tag:junk | xargs -r0 rm"))
|
|
||||||
:config
|
|
||||||
(defun +mail/flag-thread (&optional unflag beg end)
|
|
||||||
(interactive (cons current-prefix-arg (notmuch-interactive-region)))
|
|
||||||
(notmuch-search-tag
|
|
||||||
(notmuch-tag-change-list '("-inbox" "+flagged") unflag) beg end)
|
|
||||||
(when (eq beg end)
|
|
||||||
(notmuch-search-next-thread)))
|
|
||||||
|
|
||||||
(advice-add #'notmuch-poll-and-refresh-this-buffer :after
|
|
||||||
#'+mail/trash-junk)
|
|
||||||
(with-eval-after-load "evil-collection"
|
|
||||||
(evil-collection-notmuch-setup)))
|
|
||||||
#+end_src
|
|
||||||
** Smtpmail
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package smtpmail
|
|
||||||
:straight nil
|
|
||||||
:commands mail-send
|
|
||||||
:init
|
|
||||||
(setq-default
|
|
||||||
smtpmail-smtp-server "mail.aryadevchavali.com"
|
|
||||||
smtpmail-smtp-user "aryadev"
|
|
||||||
smtpmail-smtp-service 587
|
|
||||||
smtpmail-stream-type 'starttls
|
|
||||||
send-mail-function #'smtpmail-send-it
|
|
||||||
message-send-mail-function #'smtpmail-send-it))
|
|
||||||
#+end_src
|
|
||||||
* Dired
|
|
||||||
Setup for dired. Make dired-hide-details-mode the default mode when
|
|
||||||
using dired-mode, as it removes the clutter. Setup evil collection
|
|
||||||
for dired (even though dired doesn't really conflict with evil, there
|
|
||||||
are some corners I'd like to adjust).
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package dired
|
|
||||||
:straight nil
|
|
||||||
:commands (dired find-dired)
|
|
||||||
:hook
|
|
||||||
(dired-mode-hook . auto-revert-mode)
|
|
||||||
(dired-mode-hook . dired-hide-details-mode)
|
|
||||||
:init
|
|
||||||
(setq-default dired-listing-switches "-AFBlu --group-directories-first"
|
|
||||||
dired-omit-files "^\\."
|
|
||||||
dired-dwim-target t
|
|
||||||
image-dired-external-viewer "nsxiv")
|
|
||||||
(with-eval-after-load "evil-collection"
|
|
||||||
(evil-collection-dired-setup))
|
|
||||||
:general
|
|
||||||
(nmmap
|
|
||||||
:keymaps 'dired-mode-map
|
|
||||||
"SPC" nil
|
|
||||||
"SPC ," nil
|
|
||||||
"T" #'dired-create-empty-file
|
|
||||||
"H" #'dired-up-directory
|
|
||||||
"L" #'dired-find-file)
|
|
||||||
(dir-leader
|
|
||||||
"f" #'find-dired
|
|
||||||
"d" #'dired
|
|
||||||
"D" #'dired-other-window
|
|
||||||
"i" #'image-dired
|
|
||||||
"p" `(,(proc (interactive)
|
|
||||||
(dired "~/Text/PDFs/"))
|
|
||||||
:which-key "Open PDFs"))
|
|
||||||
(local-leader
|
|
||||||
:keymaps 'dired-mode-map
|
|
||||||
"i" #'dired-maybe-insert-subdir
|
|
||||||
"I" #'+dired/insert-all-subdirectories
|
|
||||||
"k" #'dired-prev-subdir
|
|
||||||
"j" #'dired-next-subdir
|
|
||||||
"K" #'dired-kill-subdir
|
|
||||||
"m" #'dired-mark-files-regexp
|
|
||||||
"u" #'dired-undo)
|
|
||||||
(nmmap
|
|
||||||
:keymaps 'image-dired-thumbnail-mode-map
|
|
||||||
"h" #'image-dired-backward-image
|
|
||||||
"l" #'image-dired-forward-image
|
|
||||||
"j" #'image-dired-next-line
|
|
||||||
"k" #'image-dired-previous-line
|
|
||||||
"H" #'image-dired-display-previous
|
|
||||||
"L" #'image-dired-display-next
|
|
||||||
"RET" #'image-dired-display-this
|
|
||||||
"m" #'image-dired-mark-thumb-original-file
|
|
||||||
"q" #'quit-window)
|
|
||||||
:config
|
|
||||||
(add-to-list 'dired-guess-shell-alist-user '("\\.pdf\\'" "zathura"))
|
|
||||||
(defun +dired/insert-all-subdirectories ()
|
|
||||||
"Insert all subdirectories currently viewable."
|
|
||||||
(interactive)
|
|
||||||
(dired-mark-directories nil)
|
|
||||||
(mapc #'dired-insert-subdir (dired-get-marked-files))
|
|
||||||
(dired-unmark-all-marks)))
|
|
||||||
#+end_src
|
|
||||||
** fd-dired
|
|
||||||
Uses fd for finding file results in a directory: ~find-dired~ ->
|
|
||||||
~fd-dired~.
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package fd-dired
|
|
||||||
:after dired
|
|
||||||
:straight t
|
|
||||||
:general
|
|
||||||
(dir-leader
|
|
||||||
"g" #'fd-dired))
|
|
||||||
#+end_src
|
|
||||||
** wdired
|
|
||||||
Similar to [[file:config.org::*(Rip)grep][wgrep]] =wdired= provides
|
|
||||||
the ability to use Emacs motions and editing on file names. This
|
|
||||||
makes stuff like mass renaming and other file management tasks way
|
|
||||||
easier than even using the mark based system.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package wdired
|
|
||||||
:after dired
|
|
||||||
:straight t
|
|
||||||
:general
|
|
||||||
(nmmap
|
|
||||||
:keymaps 'dired-mode-map
|
|
||||||
"W" #'wdired-change-to-wdired-mode)
|
|
||||||
(nmmap
|
|
||||||
:keymaps 'wdired-mode-map
|
|
||||||
"ZZ" #'wdired-finish-edit
|
|
||||||
"ZQ" #'wdired-abort-changes))
|
|
||||||
#+end_src
|
|
||||||
* WAIT Xwidget
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
Xwidget is a package which allows for the insertion of arbitrary
|
|
||||||
xwidgets into Emacs through buffers. It must be compiled into Emacs
|
|
||||||
so you might need to customise your install. One of its premier uses
|
|
||||||
is in navigating the web which it provides through the function
|
|
||||||
~xwidget-webkit-browse-url~. This renders a fully functional web
|
|
||||||
browser within Emacs.
|
|
||||||
|
|
||||||
Though I am not to keen on using Emacs to browse the web /via/ xwidget
|
|
||||||
(EWW does a good job on its own), I am very interested in its
|
|
||||||
capability to render pages with JavaScript, as it may come of use when
|
|
||||||
doing web development. I can see the results of work very quickly
|
|
||||||
without switching windows all within Emacs.
|
|
||||||
|
|
||||||
2023-10-20: Disabled as it didn't seem to work, and honestly wasn't
|
|
||||||
that useful.
|
|
||||||
** Xwidget Core
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package xwidget
|
|
||||||
:straight nil
|
|
||||||
:general
|
|
||||||
(app-leader
|
|
||||||
"u" #'xwidget-webkit-browse-url)
|
|
||||||
(nmmap
|
|
||||||
:keymaps 'xwidget-webkit-mode-map
|
|
||||||
"q" #'quit-window
|
|
||||||
"h" #'xwidget-webkit-scroll-backward
|
|
||||||
"j" #'xwidget-webkit-scroll-up
|
|
||||||
"k" #'xwidget-webkit-scroll-down
|
|
||||||
"l" #'xwidget-webkit-scroll-forward
|
|
||||||
"+" #'xwidget-webkit-zoom-in
|
|
||||||
"-" #'xwidget-webkit-zoom-out
|
|
||||||
(kbd "C-f") #'xwidget-webkit-scroll-up
|
|
||||||
(kbd "C-b") #'xwidget-webkit-scroll-down
|
|
||||||
"H" #'xwidget-webkit-back
|
|
||||||
"L" #'xwidget-webkit-forward
|
|
||||||
"gu" #'xwidget-webkit-browse-url
|
|
||||||
"gr" #'xwidget-webkit-reload
|
|
||||||
"gg" #'xwidget-webkit-scroll-top
|
|
||||||
"G" #'xwidget-webkit-scroll-bottom))
|
|
||||||
#+end_src
|
|
||||||
** Xwidget Extensions
|
|
||||||
Define a function ~+xwidget/render-file~ that reads a file name and
|
|
||||||
presents it in an xwidget. If the current file is an HTML file, ask
|
|
||||||
if user wants to open current file. Bind it to ~aU~ in the leader.
|
|
||||||
|
|
||||||
Also define a function ~+xwidget/search-query~ that first asks the
|
|
||||||
user what search engine they want to use ([[https://duckduckgo.com][Duck Duck Go]] and [[https://devdocs.io][DevDocs]]
|
|
||||||
currently) then asks for a query, which it parses then presents in an
|
|
||||||
xwidget window. Bind to ~as~ in the leader.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package xwidget
|
|
||||||
:straight nil
|
|
||||||
:commands (+xwidget/render-file +xwidget/search)
|
|
||||||
:general
|
|
||||||
(app-leader
|
|
||||||
"U" #'+xwidget/render-file
|
|
||||||
"s" #'+xwidget/search)
|
|
||||||
:config
|
|
||||||
(setenv "WEBKIT_FORCE_SANDBOX" "0")
|
|
||||||
(defun +xwidget/render-file (&optional FORCE)
|
|
||||||
"Find file (or use current file) and render in xwidget."
|
|
||||||
(interactive)
|
|
||||||
(cond
|
|
||||||
((and (not FORCE) (or (string= (replace-regexp-in-string ".*.html"
|
|
||||||
"html" (buffer-name)) "html")
|
|
||||||
(eq major-mode 'web-mode)
|
|
||||||
(eq major-mode 'html-mode))) ; If in html file
|
|
||||||
(if (y-or-n-p "Open current file?: ") ; Maybe they want to open a separate file
|
|
||||||
(xwidget-webkit-browse-url (format "file://%s" (buffer-file-name)))
|
|
||||||
(+xwidget/render-file t))) ; recurse and open file via prompt
|
|
||||||
(t
|
|
||||||
(xwidget-webkit-browse-url
|
|
||||||
(format "file://%s" (read-file-name "Enter file to open: "))))))
|
|
||||||
|
|
||||||
(defun +xwidget/search ()
|
|
||||||
"Run a search query on some search engine and display in
|
|
||||||
xwidget."
|
|
||||||
(interactive)
|
|
||||||
(let* ((engine (completing-read "Engine: " '("duckduckgo.com" "devdocs.io") nil t))
|
|
||||||
(query-raw (read-string "Enter query: "))
|
|
||||||
(query
|
|
||||||
(cond
|
|
||||||
((string= engine "duckduckgo.com") query-raw)
|
|
||||||
((string= engine "devdocs.io") (concat "_ " query-raw)))))
|
|
||||||
(xwidget-webkit-browse-url (concat "https://" engine "/?q=" query)))))
|
|
||||||
#+end_src
|
|
||||||
* Eshell
|
|
||||||
** Why Eshell?
|
|
||||||
Eshell is an integrated shell environment for Emacs, written in Emacs
|
|
||||||
Lisp. I argue that it is the best shell/command interpreter to use in
|
|
||||||
Emacs.
|
|
||||||
|
|
||||||
Eshell is unlike the alternatives in Emacs as it's a /shell/ first,
|
|
||||||
not a terminal emulator. It has the ability to spoof some aspects of a
|
|
||||||
terminal emulator (through the shell parser), but it is NOT a terminal
|
|
||||||
emulator.
|
|
||||||
|
|
||||||
The killer benefits of eshell (which would appeal to Emacs users) are
|
|
||||||
a direct result of eshell being written in Emacs lisp:
|
|
||||||
- incredible integration with Emacs utilities (such as ~dired~,
|
|
||||||
~find-file~, any read functions, etc)
|
|
||||||
- very extensible, easy to write new commands which leverage Emacs
|
|
||||||
commands as well as external utilities
|
|
||||||
- agnostic of platform: "eshell/cd" will call the underlying change
|
|
||||||
directory function for you, so commands will (usually) mean the same
|
|
||||||
thing regardless of platform
|
|
||||||
- this means as long as Emacs can run on an operating system, one
|
|
||||||
may run eshell
|
|
||||||
|
|
||||||
However, my favourite feature of eshell is the set of evaluators that
|
|
||||||
run on command input. Some of the benefits listed above come as a
|
|
||||||
result of this powerful feature. These evaluators are described below.
|
|
||||||
|
|
||||||
Lisp evaluator: works on braced expressions, evaluating them as Lisp
|
|
||||||
expressions (e.g. ~(message "Hello, World!\n")~). Any returned
|
|
||||||
objects are printed. This makes eshell a LISP REPL!
|
|
||||||
|
|
||||||
External evaluator: works within curly braces, evaluating them via
|
|
||||||
some external shell process (like sh) (e.g. ~{echo "Hello,
|
|
||||||
world!\n"}~). This makes eshell a (kinda dumb) terminal emulator!
|
|
||||||
|
|
||||||
The alias evaluator is the top level evaluator. It is the main
|
|
||||||
evaluator for each expression given to eshell. When given an
|
|
||||||
expression it tries to evaluate it by testing against these conditions:
|
|
||||||
- it's an alias defined by the user or in the ~eshell/~ namespace of
|
|
||||||
functions (simplest evaluator)
|
|
||||||
- it's some form of lisp expression (lisp evaluator)
|
|
||||||
- it's an external command (bash evaluator)
|
|
||||||
Essentially, you get the best of both Emacs and external shell
|
|
||||||
programs *ALL WITHIN* Emacs for free.
|
|
||||||
** Eshell functionality
|
|
||||||
Bind some evil-like movements for easy shell usage, and a toggle
|
|
||||||
function to pull up the eshell quickly.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package eshell
|
|
||||||
:defer t
|
|
||||||
:general
|
|
||||||
(shell-leader
|
|
||||||
"t" #'eshell)
|
|
||||||
:init
|
|
||||||
(add-hook
|
|
||||||
'eshell-mode-hook
|
|
||||||
(proc
|
|
||||||
(interactive)
|
|
||||||
(general-def
|
|
||||||
:states '(normal insert)
|
|
||||||
:keymaps 'eshell-mode-map
|
|
||||||
"M-j" #'eshell-next-matching-input-from-input
|
|
||||||
"M-k" #'eshell-previous-matching-input-from-input)
|
|
||||||
(local-leader
|
|
||||||
:keymaps 'eshell-mode-map
|
|
||||||
"c" (proc (interactive) (eshell/clear)
|
|
||||||
(recenter))
|
|
||||||
"k" #'eshell-kill-process))))
|
|
||||||
#+end_src
|
|
||||||
** Eshell pretty symbols and display
|
|
||||||
Pretty symbols and a display record.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package eshell
|
|
||||||
:defer t
|
|
||||||
:pretty
|
|
||||||
(eshell-mode-hook
|
|
||||||
("lambda" . "λ")
|
|
||||||
("numberp" . "ℤ")
|
|
||||||
("t" . "⊨")
|
|
||||||
("nil" . "Ø"))
|
|
||||||
:display
|
|
||||||
("\\*e?shell\\*" ; for general shells as well
|
|
||||||
(display-buffer-at-bottom)
|
|
||||||
(window-height . 0.33)))
|
|
||||||
#+end_src
|
|
||||||
** Eshell variables and aliases
|
|
||||||
Set some sane defaults, a banner and a prompt. The prompt checks for
|
|
||||||
a git repo in the current directory and provides some extra
|
|
||||||
information in that case (in particular, branch name and if there any
|
|
||||||
changes that haven't been committed).
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package eshell
|
|
||||||
:defer t
|
|
||||||
:config
|
|
||||||
(defun +eshell/--git-get-remote-status ()
|
|
||||||
(let* ((branch-status (split-string
|
|
||||||
(shell-command-to-string "git status | grep 'Your branch is'")))
|
|
||||||
(status (nth 3 branch-status))
|
|
||||||
(diff (cl-position "by" branch-status :test #'string=)))
|
|
||||||
(if (null diff)
|
|
||||||
(propertize "=" 'font-lock-face '(:foreground "green"))
|
|
||||||
(let ((n (nth (+ 1 diff) branch-status)))
|
|
||||||
(concat
|
|
||||||
(cond
|
|
||||||
((string= status "ahead")
|
|
||||||
(propertize "→ " 'font-lock-face '(:foreground "dodger blue")))
|
|
||||||
((string= status "behind")
|
|
||||||
(propertize "← " 'font-lock-face '(:foreground "orange red"))))
|
|
||||||
n)))))
|
|
||||||
|
|
||||||
(defun +eshell/--git-get-change-status ()
|
|
||||||
(let ((changed-files (- (length (split-string (shell-command-to-string "git status -s" ) "\n")) 1)))
|
|
||||||
(if (= changed-files 0)
|
|
||||||
(propertize "✓" 'font-lock-face '(:foreground "green"))
|
|
||||||
(propertize (number-to-string changed-files) 'font-lock-face '(:foreground "red")))))
|
|
||||||
|
|
||||||
(defun +eshell/get-git-properties ()
|
|
||||||
(let ((git-branch (shell-command-to-string "git branch")))
|
|
||||||
(if (or (string= git-branch "")
|
|
||||||
(not (string= "*" (substring git-branch 0 1))))
|
|
||||||
""
|
|
||||||
(format
|
|
||||||
"(%s<%s>[%s])"
|
|
||||||
(nth 2 (split-string git-branch "\n\\|\\*\\| "))
|
|
||||||
(+eshell/--git-get-change-status)
|
|
||||||
(+eshell/--git-get-remote-status)))))
|
|
||||||
|
|
||||||
(defun +eshell/prompt-function ()
|
|
||||||
(let ((git (+eshell/get-git-properties)))
|
|
||||||
(mapconcat
|
|
||||||
(lambda (item)
|
|
||||||
(if (listp item)
|
|
||||||
(propertize (car item)
|
|
||||||
'read-only t
|
|
||||||
'font-lock-face (cdr item)
|
|
||||||
'front-sticky '(font-lock-face read-only)
|
|
||||||
'rear-nonsticky '(font-lock-face read-only))
|
|
||||||
item))
|
|
||||||
(list
|
|
||||||
'("[")
|
|
||||||
`(,(abbreviate-file-name (eshell/pwd)) :foreground "LimeGreen")
|
|
||||||
'("]")
|
|
||||||
(if (string= git "")
|
|
||||||
""
|
|
||||||
(concat "-" git ""))
|
|
||||||
"\n"
|
|
||||||
`(,(format-time-string "[%H:%M:%S]") :foreground "purple")
|
|
||||||
"\n"
|
|
||||||
'("𝜆> " :foreground "DeepSkyBlue")))))
|
|
||||||
|
|
||||||
(defun +eshell/banner-message ()
|
|
||||||
(concat (shell-command-to-string "~/.local/scripts/cowfortune")
|
|
||||||
"\n"))
|
|
||||||
|
|
||||||
(setq eshell-cmpl-ignore-case t
|
|
||||||
eshell-cd-on-directory t
|
|
||||||
eshell-banner-message '(+eshell/banner-message)
|
|
||||||
eshell-highlight-prompt nil
|
|
||||||
eshell-prompt-function #'+eshell/prompt-function
|
|
||||||
eshell-prompt-regexp "^𝜆> "))
|
|
||||||
#+end_src
|
|
||||||
** Eshell change directory quickly
|
|
||||||
Add ~eshell/goto~, which is actually a command accessible from within
|
|
||||||
eshell (this is because ~eshell/*~ creates an accessible function
|
|
||||||
within eshell with name ~*~). ~eshell/goto~ makes it easier to change
|
|
||||||
directories by using Emacs' find-file interface (which is much faster
|
|
||||||
than ~cd ..; ls -l~).
|
|
||||||
|
|
||||||
~eshell/goto~ is a better ~cd~ for eshell. However it is really just
|
|
||||||
a plaster over a bigger issue for my workflow; many times I want
|
|
||||||
eshell to be present in the current directory of the buffer I am
|
|
||||||
using. So here's also a command for opening eshell with the current
|
|
||||||
directory.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package eshell
|
|
||||||
:defer t
|
|
||||||
:general
|
|
||||||
(leader
|
|
||||||
"T" #'+eshell/current-buffer)
|
|
||||||
:config
|
|
||||||
(defun eshell/goto (&rest args)
|
|
||||||
"Use `read-directory-name' to change directories."
|
|
||||||
(eshell/cd (list (read-directory-name "Directory?: "))))
|
|
||||||
|
|
||||||
(defun eshell/project-root (&rest args)
|
|
||||||
"Change to directory `project-root'"
|
|
||||||
(if (project-current)
|
|
||||||
(eshell/cd (list (project-root (project-current))))
|
|
||||||
(eshell/echo (format "[%s]: No project in current directory"
|
|
||||||
(propertize "Error" 'font-lock-face '(:foreground "red"))))))
|
|
||||||
|
|
||||||
(defun +eshell/current-buffer ()
|
|
||||||
(interactive)
|
|
||||||
(let ((dir (if buffer-file-name
|
|
||||||
(file-name-directory buffer-file-name)
|
|
||||||
default-directory))
|
|
||||||
(buf (eshell)))
|
|
||||||
(if dir
|
|
||||||
(with-current-buffer buf
|
|
||||||
(eshell/cd dir)
|
|
||||||
(eshell-send-input))
|
|
||||||
(message "Could not switch eshell: buffer is not real file")))))
|
|
||||||
#+end_src
|
|
||||||
* WAIT Elfeed
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
Elfeed is the perfect RSS feed reader, integrated into Emacs
|
|
||||||
perfectly. I've got a set of feeds that I use for a large variety of
|
|
||||||
stuff, mostly media and entertainment. I've also bound "<leader> ar"
|
|
||||||
to elfeed for loading the system.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package elfeed
|
|
||||||
:general
|
|
||||||
(app-leader "r" #'elfeed)
|
|
||||||
(nmmap
|
|
||||||
:keymaps 'elfeed-search-mode-map
|
|
||||||
"gr" #'elfeed-update
|
|
||||||
"s" #'elfeed-search-live-filter
|
|
||||||
"<return>" #'elfeed-search-show-entry)
|
|
||||||
:init
|
|
||||||
(setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/"))
|
|
||||||
|
|
||||||
(setq +rss/feed-urls
|
|
||||||
'(("Arch Linux"
|
|
||||||
"https://www.archlinux.org/feeds/news/"
|
|
||||||
News Technology)
|
|
||||||
("The Onion"
|
|
||||||
"https://www.theonion.com/rss"
|
|
||||||
Social)
|
|
||||||
("Protesilaos Stavrou"
|
|
||||||
"https://www.youtube.com/@protesilaos"
|
|
||||||
YouTube Technology)
|
|
||||||
("Tsoding Daily"
|
|
||||||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg"
|
|
||||||
YouTube Technology)
|
|
||||||
("Tsoding"
|
|
||||||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCrqM0Ym_NbK1fqeQG2VIohg"
|
|
||||||
YouTube Technology)
|
|
||||||
("Nexpo"
|
|
||||||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw"
|
|
||||||
YouTube Stories)
|
|
||||||
("3B1B"
|
|
||||||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw"
|
|
||||||
YouTube)
|
|
||||||
("Fredrik Knusden"
|
|
||||||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCbWcXB0PoqOsAvAdfzWMf0w"
|
|
||||||
YouTube Stories)
|
|
||||||
("Barely Sociable"
|
|
||||||
"https://www.youtube.com/feeds/videos.xml?channel_id=UC9PIn6-XuRKZ5HmYeu46AIw"
|
|
||||||
YouTube Stories)
|
|
||||||
("Atrocity Guide"
|
|
||||||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCn8OYopT9e8tng-CGEWzfmw"
|
|
||||||
YouTube Stories)
|
|
||||||
("Hacker News"
|
|
||||||
"https://news.ycombinator.com/rss"
|
|
||||||
Social News Technology)
|
|
||||||
("Hacker Factor"
|
|
||||||
"https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2"
|
|
||||||
Social)))
|
|
||||||
:config
|
|
||||||
(with-eval-after-load "evil-collection"
|
|
||||||
(evil-collection-elfeed-setup))
|
|
||||||
|
|
||||||
(setq elfeed-feeds (cl-map 'list #'(lambda (item)
|
|
||||||
(append (list (nth 1 item)) (cdr (cdr item))))
|
|
||||||
+rss/feed-urls))
|
|
||||||
|
|
||||||
(advice-add 'elfeed-search-show-entry :after #'+elfeed/dispatch-entry)
|
|
||||||
|
|
||||||
(defun +elfeed/dispatch-entry (entry)
|
|
||||||
"Process each type of entry differently.
|
|
||||||
e.g., you may want to open HN entries in eww."
|
|
||||||
(let ((url (elfeed-entry-link entry)))
|
|
||||||
(pcase url
|
|
||||||
((pred (string-match-p "https\\:\\/\\/www.youtube.com\\/watch"))
|
|
||||||
(mpv-play-url url))
|
|
||||||
(_ (eww url))))))
|
|
||||||
#+end_src
|
|
||||||
* Magit
|
|
||||||
Magit is *the* git porcelain for Emacs, which perfectly encapsulates
|
|
||||||
the git cli. In this case I just need to setup the bindings for it.
|
|
||||||
As magit will definitely load after evil (as it must be run by a
|
|
||||||
binding, and evil will load after init), I can use evil-collection
|
|
||||||
freely. Also, define an auto insert for commit messages so that I
|
|
||||||
don't need to write everything myself.
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package magit
|
|
||||||
:defer t
|
|
||||||
:display
|
|
||||||
("magit:.*"
|
|
||||||
(display-buffer-same-window)
|
|
||||||
(inhibit-duplicate-buffer . t))
|
|
||||||
("magit-diff:.*"
|
|
||||||
(display-buffer-below-selected))
|
|
||||||
("magit-log:.*"
|
|
||||||
(display-buffer-same-window))
|
|
||||||
:general
|
|
||||||
(leader
|
|
||||||
"g" '(magit-dispatch :which-key "Magit"))
|
|
||||||
(code-leader
|
|
||||||
"b" #'magit-blame)
|
|
||||||
:auto-insert
|
|
||||||
(("COMMIT_EDITMSG" . "Commit skeleton")
|
|
||||||
""
|
|
||||||
"(" (read-string "Enter feature/module: ") ")"
|
|
||||||
(read-string "Enter simple description: ") "\n\n")
|
|
||||||
:init
|
|
||||||
(setq vc-follow-symlinks t
|
|
||||||
magit-blame-echo-style 'lines
|
|
||||||
magit-copy-revision-abbreviated t)
|
|
||||||
:config
|
|
||||||
(with-eval-after-load "evil"
|
|
||||||
(evil-set-initial-state 'magit-status-mode 'motion))
|
|
||||||
(with-eval-after-load "evil-collection"
|
|
||||||
(evil-collection-magit-setup)))
|
|
||||||
#+end_src
|
|
||||||
* IBuffer
|
|
||||||
IBuffer is the dired of buffers: providing the ability to mark
|
|
||||||
buffers, mass rename/delete and just observe stuff.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package ibuffer
|
|
||||||
:general
|
|
||||||
(buffer-leader
|
|
||||||
"i" #'ibuffer)
|
|
||||||
:config
|
|
||||||
(with-eval-after-load "evil-collection"
|
|
||||||
(evil-collection-ibuffer-setup)))
|
|
||||||
#+end_src
|
|
||||||
* Proced
|
|
||||||
Emacs has two systems for process management:
|
|
||||||
+ proced: a general 'top' like interface which allows general
|
|
||||||
management of linux processes
|
|
||||||
+ list-processes: a specific Emacs based system that lists processes
|
|
||||||
spawned by Emacs (similar to a top for Emacs specifically)
|
|
||||||
|
|
||||||
Core proced config, just a few bindings and evil collection setup.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package proced
|
|
||||||
:straight nil
|
|
||||||
:general
|
|
||||||
(app-leader
|
|
||||||
"p" #'proced)
|
|
||||||
(nmap
|
|
||||||
:keymaps 'proced-mode-map
|
|
||||||
"za" #'proced-toggle-auto-update)
|
|
||||||
:display
|
|
||||||
("\\*Proced\\*"
|
|
||||||
(display-buffer-at-bottom)
|
|
||||||
(window-height . 0.25))
|
|
||||||
:init
|
|
||||||
(setq proced-auto-update-interval 0.5)
|
|
||||||
:config
|
|
||||||
(with-eval-after-load "evil-collection"
|
|
||||||
(evil-collection-proced-setup)))
|
|
||||||
#+end_src
|
|
||||||
* Calculator
|
|
||||||
Surprise, surprise Emacs comes with a calculator.
|
|
||||||
|
|
||||||
Greater surprise, this thing is over powered. It can perform the
|
|
||||||
following (and more):
|
|
||||||
- Matrix calculations
|
|
||||||
- Generalised calculus operations
|
|
||||||
- Equation solvers for n-degree multi-variable polynomials
|
|
||||||
- Embedded mode (check below)!
|
|
||||||
|
|
||||||
~calc-mode~ is a calculator system within Emacs that provides a
|
|
||||||
diverse array of mathematical operations. It uses reverse polish
|
|
||||||
notation to do calculations (though there is a standard infix
|
|
||||||
algebraic notation mode).
|
|
||||||
|
|
||||||
Embedded mode allows computation with the current buffer as the echo
|
|
||||||
area. This basically means I can compute stuff within a buffer
|
|
||||||
without invoking calc directly: $1 + 2\rightarrow_{\text{calc-embed}} 3$.
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package calc
|
|
||||||
:straight nil
|
|
||||||
:display
|
|
||||||
("*Calculator*"
|
|
||||||
(display-buffer-at-bottom)
|
|
||||||
(window-height . 0.18))
|
|
||||||
:general
|
|
||||||
(app-leader
|
|
||||||
"c" #'calc-dispatch)
|
|
||||||
(mode-leader
|
|
||||||
"c" #'calc-embedded)
|
|
||||||
:init
|
|
||||||
(setq calc-algebraic-mode t)
|
|
||||||
:config
|
|
||||||
(with-eval-after-load "evil-collection"
|
|
||||||
(evil-collection-calc-setup)))
|
|
||||||
#+end_src
|
|
||||||
** WAIT Calctex
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
~calc-mode~ also has a 3rd party package called ~calctex~. It renders
|
|
||||||
mathematical expressions within calc as if they were rendered in TeX.
|
|
||||||
You can also copy the expressions in their TeX forms, which is pretty
|
|
||||||
useful when writing a paper. I've set a very specific lock on this
|
|
||||||
repository as it's got quite a messy work-tree and this commit seems to
|
|
||||||
work for me given the various TeX utilities installed via Arch.
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package calctex
|
|
||||||
:after calc
|
|
||||||
:straight (calctex :type git :host github :repo "johnbcoughlin/calctex")
|
|
||||||
:hook (calc-mode-hook . calctex-mode))
|
|
||||||
#+end_src
|
|
||||||
* WAIT Ledger
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package ledger-mode
|
|
||||||
:defer t)
|
|
||||||
|
|
||||||
(use-package evil-ledger
|
|
||||||
:after ledger-mode)
|
|
||||||
#+end_src
|
|
||||||
* Zone
|
|
||||||
Of course Emacs has a cool screensaver software.
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package zone-matrix
|
|
||||||
:straight t
|
|
||||||
:commands (zone)
|
|
||||||
:general
|
|
||||||
(leader
|
|
||||||
"z" #'zone)
|
|
||||||
:init
|
|
||||||
(setq zone-programs
|
|
||||||
[zone-pgm-drip
|
|
||||||
zone-pgm-drip-fretfully
|
|
||||||
zone-pgm-martini-swan-dive
|
|
||||||
zone-pgm-stress
|
|
||||||
zone-pgm-random-life]))
|
|
||||||
#+end_src
|
|
||||||
* (Wo)man
|
|
||||||
Man pages are the user manuals for most software on Linux. Really
|
|
||||||
useful when writing code for Un*x systems, though they can be very
|
|
||||||
verbose.
|
|
||||||
|
|
||||||
2023-08-17: `Man-notify-method' is the reason the `:display' record
|
|
||||||
doesn't work here. I think it's to do with how Man pages are rendered
|
|
||||||
or something, but very annoying as it's a break from standards!
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package man
|
|
||||||
:demand t
|
|
||||||
:straight nil
|
|
||||||
:init
|
|
||||||
(setq Man-notify-method 'pushy)
|
|
||||||
:display
|
|
||||||
("^\\*Man.*"
|
|
||||||
(display-buffer-reuse-mode-window display-buffer-same-window))
|
|
||||||
:general
|
|
||||||
(file-leader
|
|
||||||
"m" #'man) ;; kinda like "find man page"
|
|
||||||
(nmmap
|
|
||||||
:keymaps 'Man-mode-map
|
|
||||||
"RET" #'man-follow))
|
|
||||||
#+end_src
|
|
||||||
* WAIT gif-screencast
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
Little application that uses =gifsicle= to make essentially videos of
|
|
||||||
Emacs. Useful for demonstrating features.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package gif-screencast
|
|
||||||
:straight t
|
|
||||||
:general
|
|
||||||
(app-leader
|
|
||||||
"x" #'gif-screencast-start-or-stop)
|
|
||||||
:init
|
|
||||||
(setq gif-screencast-output-directory (expand-file-name "~/Media/emacs/")))
|
|
||||||
#+end_src
|
|
||||||
* Image-mode
|
|
||||||
Image mode, for viewing images. Supports tons of formats, easy to use
|
|
||||||
and integrates slickly into image-dired. Of course,
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package image-mode
|
|
||||||
:straight nil
|
|
||||||
:general
|
|
||||||
(nmmap
|
|
||||||
:keymaps 'image-mode-map
|
|
||||||
"+" #'image-increase-size
|
|
||||||
"-" #'image-decrease-size
|
|
||||||
"p" #'image-animate
|
|
||||||
"P" #'image-animate-set-speed
|
|
||||||
"h" #'image-backward-hscroll
|
|
||||||
"j" #'image-next-line
|
|
||||||
"k" #'image-previous-line
|
|
||||||
"l" #'image-forward-hscroll))
|
|
||||||
#+end_src
|
|
||||||
* WAIT ERC
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package erc
|
|
||||||
:defer t
|
|
||||||
:init
|
|
||||||
(setq erc-server "irc.libera.chat"
|
|
||||||
erc-nick "oreodave"
|
|
||||||
erc-buffer-display "current"))
|
|
||||||
#+end_src
|
|
||||||
* MPV
|
|
||||||
Basically a porcelain over mpv via the IPC interface.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package mpv
|
|
||||||
:defer t
|
|
||||||
:straight t
|
|
||||||
:config
|
|
||||||
(with-eval-after-load "org"
|
|
||||||
(defun org-mpv-complete-link (&optional arg)
|
|
||||||
(replace-regexp-in-string
|
|
||||||
"file:" "mpv:"
|
|
||||||
(org-link-complete-file arg)
|
|
||||||
t t))
|
|
||||||
(org-link-set-parameters "mpv"
|
|
||||||
:follow #'mpv-play :complete #'org-mpv-complete-link)))
|
|
||||||
#+end_src
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,808 +0,0 @@
|
|||||||
#+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. Window management, Keybindings or completion
|
|
||||||
frameworks; they're all done here.
|
|
||||||
* General
|
|
||||||
General provides a great solution for binding keys. It has evil and
|
|
||||||
use-package support so it fits nicely into configuration. In this
|
|
||||||
case, I define a "definer" for the "LEADER" keys. Leader is bound to
|
|
||||||
~SPC~ and it's functionally equivalent to the doom/spacemacs leader.
|
|
||||||
Local leader is bound to ~SPC ,~ and it's similar to doom/spacemacs
|
|
||||||
leader but doesn't try to fully assimilate the local-leader map,
|
|
||||||
instead just picking stuff I think is useful. This forces me to learn
|
|
||||||
only as many bindings as I find necessary; no more, no less.
|
|
||||||
|
|
||||||
I also define prefix leaders for differing applications. These are
|
|
||||||
quite self explanatory by their name and provide a nice way to
|
|
||||||
visualise all bindings under a specific heading just by searching the
|
|
||||||
code.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package general
|
|
||||||
:straight t
|
|
||||||
:demand t
|
|
||||||
:config
|
|
||||||
;; General which key definitions for leaders
|
|
||||||
(general-def
|
|
||||||
:states '(normal motion)
|
|
||||||
"SPC" 'nil
|
|
||||||
"\\" '(nil :which-key "Local leader")
|
|
||||||
"SPC a" '(nil :which-key "Applications")
|
|
||||||
"SPC b" '(nil :which-key "Buffers")
|
|
||||||
"SPC c" '(nil :which-key "Code")
|
|
||||||
"SPC d" '(nil :which-key "Directories")
|
|
||||||
"SPC f" '(nil :which-key "Files")
|
|
||||||
"SPC i" '(nil :which-key "Insert")
|
|
||||||
"SPC m" '(nil :which-key "Modes")
|
|
||||||
"SPC r" '(nil :which-key "Tabs")
|
|
||||||
"SPC s" '(nil :which-key "Search")
|
|
||||||
"SPC t" '(nil :which-key "Shell")
|
|
||||||
"SPC q" '(nil :which-key "Quit/Literate"))
|
|
||||||
|
|
||||||
(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 tab-leader
|
|
||||||
:states '(normal motion)
|
|
||||||
:keymaps 'override
|
|
||||||
:prefix "SPC r")
|
|
||||||
|
|
||||||
(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 for Emacs
|
|
||||||
Some bindings that I couldn't fit elsewhere easily.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package emacs
|
|
||||||
:straight nil
|
|
||||||
:general
|
|
||||||
("C-x d" #'delete-frame)
|
|
||||||
|
|
||||||
(nmmap
|
|
||||||
"M-;" #'eval-expression
|
|
||||||
"g=" #'align-regexp
|
|
||||||
"C--" #'text-scale-decrease
|
|
||||||
"C-=" #'text-scale-increase
|
|
||||||
"C-+" #'text-scale-adjust)
|
|
||||||
|
|
||||||
(leader
|
|
||||||
"SPC" '(execute-extended-command :which-key "M-x")
|
|
||||||
"p" `(,project-prefix-map :which-key "Project")
|
|
||||||
"'" '(browse-url-emacs :which-key "Download URL to Emacs")
|
|
||||||
":" `(,(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)
|
|
||||||
|
|
||||||
(quit-leader
|
|
||||||
"q" #'save-buffers-kill-terminal
|
|
||||||
"c" #'+literate/compile-config
|
|
||||||
"C" #'+literate/clean-config
|
|
||||||
"l" #'+literate/load-config)
|
|
||||||
|
|
||||||
(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
|
|
||||||
:defer t
|
|
||||||
:hook (after-init-hook . evil-mode)
|
|
||||||
:general
|
|
||||||
(leader
|
|
||||||
"w" '(evil-window-map :which-key "Window")
|
|
||||||
"wT" #'window-swap-states
|
|
||||||
"wd" #'evil-window-delete)
|
|
||||||
|
|
||||||
(nmmap
|
|
||||||
"K" #'man
|
|
||||||
"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))
|
|
||||||
#+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
|
|
||||||
** Evil number
|
|
||||||
Increment/decrement a number at point like Vim does, but use bindings
|
|
||||||
that don't conflict with Emacs default.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package evil-numbers
|
|
||||||
:after evil
|
|
||||||
:general
|
|
||||||
(nmmap
|
|
||||||
"+" #'evil-numbers/inc-at-pt
|
|
||||||
"-" #'evil-numbers/dec-at-pt))
|
|
||||||
#+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
|
|
||||||
"P" (proc (interactive)
|
|
||||||
(with-current-buffer (find-file-noselect
|
|
||||||
(concat user-emacs-directory "config.org"))
|
|
||||||
(counsel-git))))
|
|
||||||
(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
|
|
||||||
*** WAIT 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
|
|
||||||
*** WAIT 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
|
|
||||||
** WAIT 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
|
|
||||||
"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 a use-package call.
|
|
||||||
|
|
||||||
I have no idea whether it's optimal AT ALL, but it works for me.
|
|
||||||
|
|
||||||
2024-04-23: Found this option ~switch-to-buffer-obey-display-actions~
|
|
||||||
which makes manual buffer switches obey the same constraints via
|
|
||||||
~display-buffer-alist~ as creating the buffer automatically.
|
|
||||||
#+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
|
|
||||||
(setq switch-to-buffer-obey-display-actions t)
|
|
||||||
(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
|
|
||||||
* Tabs
|
|
||||||
Tabs in vscode are just like buffers in Emacs but way slower and
|
|
||||||
harder to use. Tabs in Emacs are essentially window layouts, similar
|
|
||||||
to instances in Tmux. With this setup I can use tabs quite
|
|
||||||
effectively.
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package tab-bar
|
|
||||||
:defer t
|
|
||||||
:straight nil
|
|
||||||
:init
|
|
||||||
(setq tab-bar-show 1)
|
|
||||||
:config
|
|
||||||
(tab-bar-mode)
|
|
||||||
:general
|
|
||||||
(tab-leader
|
|
||||||
"t" #'tab-switch
|
|
||||||
"j" #'tab-next
|
|
||||||
"k" #'tab-previous
|
|
||||||
"h" #'tab-move-to
|
|
||||||
"l" #'tab-move
|
|
||||||
"n" #'tab-new
|
|
||||||
"c" #'tab-close
|
|
||||||
"d" #'tab-close
|
|
||||||
"f" #'tab-detach
|
|
||||||
"w" #'tab-window-detach
|
|
||||||
"r" #'tab-rename)
|
|
||||||
(mode-leader
|
|
||||||
"t" #'toggle-tab-bar-mode-from-frame))
|
|
||||||
#+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
|
|
||||||
** WAIT 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)
|
|
||||||
(text-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
|
|
||||||
** WAIT Hydra
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
Hydra is a great package by =abo-abo= (yes the same guy who made ivy
|
|
||||||
and swiper) and I hope to use it later on in the config. There are
|
|
||||||
two use-package declarations here: one for ~hydra~ itself, and the
|
|
||||||
other for ~use-package-hydra~ which provides the keyword ~:hydra~ in
|
|
||||||
use-package declarations.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package hydra
|
|
||||||
:straight t)
|
|
||||||
|
|
||||||
(use-package use-package-hydra
|
|
||||||
:straight t)
|
|
||||||
#+end_src
|
|
||||||
@@ -55,10 +55,8 @@
|
|||||||
|
|
||||||
;; Files
|
;; Files
|
||||||
(defconst +literate/org-files
|
(defconst +literate/org-files
|
||||||
(+literate/filter
|
(mapcar #'(lambda (x) (expand-file-name (concat user-emacs-directory x)))
|
||||||
#'+literate/org-p
|
(list "config.org")))
|
||||||
(mapcar #'(lambda (file) (concat user-emacs-directory file))
|
|
||||||
(cddr (directory-files user-emacs-directory)))))
|
|
||||||
|
|
||||||
(defconst +literate/el-init-files
|
(defconst +literate/el-init-files
|
||||||
`(,(concat user-emacs-directory "early-init.el")
|
`(,(concat user-emacs-directory "early-init.el")
|
||||||
|
|||||||
@@ -1,821 +0,0 @@
|
|||||||
#+title: Programming language configuration
|
|
||||||
#+author: Aryadev Chavali
|
|
||||||
#+description: Description
|
|
||||||
#+date: 2024-04-16
|
|
||||||
#+property: header-args:emacs-lisp :tangle lang.el :comments link :results none
|
|
||||||
#+options: toc:nil
|
|
||||||
#+startup: noindent
|
|
||||||
|
|
||||||
* Makefile
|
|
||||||
Defines an auto-insert for Makefiles. Assumes C but it's very easy to
|
|
||||||
change it for C++.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package emacs
|
|
||||||
:auto-insert
|
|
||||||
(("[mM]akefile\\'" . "Makefile skeleton")
|
|
||||||
""
|
|
||||||
"CC=gcc
|
|
||||||
CFLAGS=-Wall -Wextra -Werror -Wswitch-enum -ggdb -fsanitize=address -fsanitize=undefined -std=c11
|
|
||||||
LIBS=
|
|
||||||
|
|
||||||
ARGS=
|
|
||||||
OUT=main.out
|
|
||||||
|
|
||||||
SRC=src
|
|
||||||
DIST=build
|
|
||||||
CODE=$(addprefix $(SRC)/, ) # add source files here
|
|
||||||
OBJECTS=$(CODE:$(SRC)/%.c=$(DIST)/%.o)
|
|
||||||
DEPDIR:=$(DIST)/dependencies
|
|
||||||
DEPFLAGS=-MT $@ -MMD -MP -MF
|
|
||||||
DEPS:=$(CODE:$(SRC)/%.c=$(DEPDIR):%.d) $(DEPDIR)/main.d
|
|
||||||
|
|
||||||
.PHONY: all
|
|
||||||
all: $(OUT)
|
|
||||||
|
|
||||||
$(OUT): $(DIST)/$(OUT)
|
|
||||||
|
|
||||||
$(DIST)/$(OUT): $(OBJECTS) $(DIST)/main.o | $(DIST)
|
|
||||||
$(CC) $(CFLAGS) $^ -o $@ $(LIBS)
|
|
||||||
|
|
||||||
$(DIST)/%.o: $(SRC)/%.c | $(DIST) $(DEPDIR)
|
|
||||||
$(CC) $(CFLAGS) $(DEPFLAGS) $(DEPDIR)/$*.d -c $< -o $@ $(LIBS)
|
|
||||||
|
|
||||||
.PHONY: run
|
|
||||||
run: $(DIST)/$(OUT)
|
|
||||||
./$^ $(ARGS)
|
|
||||||
|
|
||||||
.PHONY:
|
|
||||||
clean:
|
|
||||||
rm -rfv $(DIST)/*
|
|
||||||
|
|
||||||
$(DIST):
|
|
||||||
mkdir -p $(DIST)
|
|
||||||
|
|
||||||
$(DEPDIR):
|
|
||||||
mkdir -p $(DEPDIR)
|
|
||||||
|
|
||||||
-include $(DEPS)
|
|
||||||
"
|
|
||||||
_))
|
|
||||||
#+end_src
|
|
||||||
* PDF
|
|
||||||
I use PDFs mostly for reading reports or papers. Though Emacs isn't
|
|
||||||
my preferred application for viewing PDFs (I highly recommend
|
|
||||||
[[https://pwmt.org/projects/zathura/][Zathura]]), similar to most
|
|
||||||
things with Emacs, having a PDF viewer builtin can be a very useful
|
|
||||||
asset.
|
|
||||||
|
|
||||||
For example if I were editing an org document which I was eventually
|
|
||||||
compiling into a PDF, my workflow would be much smoother with a PDF
|
|
||||||
viewer within Emacs that I can open on another pane.
|
|
||||||
** WAIT PDF tools
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
~pdf-tools~ provides the necessary functionality for viewing PDFs.
|
|
||||||
There is no proper PDF viewing without this package.
|
|
||||||
~evil-collection~ provides a setup for this mode, so use that.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package pdf-tools
|
|
||||||
:mode ("\\.[pP][dD][fF]\\'" . pdf-view-mode)
|
|
||||||
:straight t
|
|
||||||
:display
|
|
||||||
("^.*pdf$"
|
|
||||||
(display-buffer-same-window)
|
|
||||||
(inhibit-duplicate-buffer . t))
|
|
||||||
:config
|
|
||||||
(pdf-tools-install-noverify)
|
|
||||||
(with-eval-after-load "evil-collection"
|
|
||||||
(evil-collection-pdf-setup)))
|
|
||||||
#+end_src
|
|
||||||
** WAIT PDF grep
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
PDF grep is a Linux tool that allows for searches against the text
|
|
||||||
inside of PDFs similar to standard grep. This cannot be performed by
|
|
||||||
standard grep due to how PDFs are encoded; they are not a clear text
|
|
||||||
format.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package pdfgrep
|
|
||||||
:after pdf-tools
|
|
||||||
:hook (pdf-view-mode-hook . pdfgrep-mode)
|
|
||||||
:general
|
|
||||||
(nmap
|
|
||||||
:keymaps 'pdf-view-mode-map
|
|
||||||
"M-g" #'pdfgrep))
|
|
||||||
#+end_src
|
|
||||||
* WAIT SQL
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
The default SQL package provides support for connecting to common
|
|
||||||
database types (sqlite, mysql, etc) for auto completion and query
|
|
||||||
execution. I don't use SQL currently but whenever I need it it's
|
|
||||||
there.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package sql
|
|
||||||
:defer t
|
|
||||||
:straight nil
|
|
||||||
:init
|
|
||||||
(setq sql-display-sqli-buffer-function nil))
|
|
||||||
#+end_src
|
|
||||||
* WAIT Ada
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
Check out [[file:elisp/ada-mode.el][ada-mode]], my custom ~ada-mode~
|
|
||||||
that replaces the default one. This mode just colourises stuff, and
|
|
||||||
uses eglot and a language server to do the hard work.
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package ada-mode
|
|
||||||
:straight nil
|
|
||||||
:load-path "elisp/"
|
|
||||||
:defer t
|
|
||||||
:config
|
|
||||||
(with-eval-after-load "eglot"
|
|
||||||
(add-hook 'ada-mode-hook #'eglot)))
|
|
||||||
#+end_src
|
|
||||||
* NHexl
|
|
||||||
Hexl-mode is the inbuilt package within Emacs to edit hex and binary
|
|
||||||
format buffers. There are a few problems with hexl-mode though,
|
|
||||||
including an annoying prompt on /revert-buffer/.
|
|
||||||
|
|
||||||
Thus, nhexl-mode! It comes with a few other improvements. Check out
|
|
||||||
the [[https://elpa.gnu.org/packages/nhexl-mode.html][page]] yourself.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package nhexl-mode
|
|
||||||
:straight t
|
|
||||||
:mode ("\\.bin" "\\.out"))
|
|
||||||
#+end_src
|
|
||||||
* C/C++
|
|
||||||
Setup for C and C++ modes, using Emacs' default package: cc-mode.
|
|
||||||
** cc-mode
|
|
||||||
Tons of stuff, namely:
|
|
||||||
+ ~auto-fill-mode~ for 80 char limit
|
|
||||||
+ Some keybindings to make evil statement movement is easy
|
|
||||||
+ Lots of pretty symbols
|
|
||||||
+ Indenting options and a nice (for me) code style for C (though
|
|
||||||
aggressive indent screws with this a bit)
|
|
||||||
+ Auto inserts to get a C file going
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package cc-mode
|
|
||||||
:defer t
|
|
||||||
:hook
|
|
||||||
(c-mode-hook . auto-fill-mode)
|
|
||||||
(c++-mode-hook . auto-fill-mode)
|
|
||||||
:general
|
|
||||||
(:keymaps '(c-mode-map c++-mode-map)
|
|
||||||
:states '(normal motion visual)
|
|
||||||
"(" #'c-beginning-of-statement
|
|
||||||
")" #'c-end-of-statement)
|
|
||||||
:pretty
|
|
||||||
(c-mode-hook
|
|
||||||
("puts" . "φ")
|
|
||||||
("fputs" . "ϕ")
|
|
||||||
("printf" . "ω")
|
|
||||||
("fprintf" . "Ω")
|
|
||||||
("NULL" . "Ø")
|
|
||||||
("true" . "⊨")
|
|
||||||
("false" . "⊭")
|
|
||||||
("!" . "¬")
|
|
||||||
("&&" . "∧")
|
|
||||||
("||" . "∨")
|
|
||||||
("for" . "∀")
|
|
||||||
("return" . "⟼"))
|
|
||||||
(c++-mode-hook
|
|
||||||
("nullptr" . "Ø")
|
|
||||||
("string" . "𝕊")
|
|
||||||
("vector" . "ℓ")
|
|
||||||
("puts" . "φ")
|
|
||||||
("fputs" . "ϕ")
|
|
||||||
("printf" . "ω")
|
|
||||||
("fprintf" . "Ω")
|
|
||||||
("NULL" . "Ø")
|
|
||||||
("true" . "⊨")
|
|
||||||
("false" . "⊭")
|
|
||||||
("!" . "¬")
|
|
||||||
("&&" . "∧")
|
|
||||||
("||" . "∨")
|
|
||||||
("for" . "∀")
|
|
||||||
("return" . "⟼"))
|
|
||||||
:init
|
|
||||||
(setq-default c-basic-offset 2)
|
|
||||||
(setq-default c-auto-newline nil)
|
|
||||||
(setq-default c-default-style '((other . "user")))
|
|
||||||
(defun +cc/copyright-notice ()
|
|
||||||
(let* ((lines (split-string (+license/copyright-notice) "\n"))
|
|
||||||
(copyright-line (car lines))
|
|
||||||
(rest (cdr lines)))
|
|
||||||
(concat
|
|
||||||
"* "
|
|
||||||
copyright-line
|
|
||||||
"\n"
|
|
||||||
(mapconcat
|
|
||||||
#'(lambda (x)
|
|
||||||
(if (string= x "")
|
|
||||||
""
|
|
||||||
(concat " * " x)))
|
|
||||||
rest
|
|
||||||
"\n"))))
|
|
||||||
|
|
||||||
:auto-insert
|
|
||||||
(("\\.c\\'" . "C skeleton")
|
|
||||||
""
|
|
||||||
"/" (+cc/copyright-notice) "\n\n"
|
|
||||||
" * Created: " (format-time-string "%Y-%m-%d") "\n"
|
|
||||||
" * Author: " user-full-name "\n"
|
|
||||||
" * Description: " _ "\n"
|
|
||||||
" */\n"
|
|
||||||
"\n")
|
|
||||||
(("\\.cpp\\'" "C++ skeleton")
|
|
||||||
""
|
|
||||||
"/" (+cc/copyright-notice) "\n\n"
|
|
||||||
" * Created: " (format-time-string "%Y-%m-%d") "\n"
|
|
||||||
" * Author: " user-full-name "\n"
|
|
||||||
" * Description: " _ "\n"
|
|
||||||
" */\n"
|
|
||||||
"\n")
|
|
||||||
(("\\.\\([Hh]\\|hh\\|hpp\\|hxx\\|h\\+\\+\\)\\'" . "C / C++ header")
|
|
||||||
(replace-regexp-in-string "[^A-Z0-9]" "_"
|
|
||||||
(string-replace "+" "P"
|
|
||||||
(upcase
|
|
||||||
(file-name-nondirectory buffer-file-name))))
|
|
||||||
"/" (+cc/copyright-notice) "\n\n"
|
|
||||||
" * Created: " (format-time-string "%Y-%m-%d") "\n"
|
|
||||||
" * Author: " user-full-name "\n"
|
|
||||||
" * Description: " _ "\n"
|
|
||||||
" */\n\n"
|
|
||||||
"#ifndef " str n "#define " str "\n\n" "\n\n#endif")
|
|
||||||
:config
|
|
||||||
(c-add-style
|
|
||||||
"user"
|
|
||||||
'((c-basic-offset . 2)
|
|
||||||
(c-comment-only-line-offset . 0)
|
|
||||||
(c-hanging-braces-alist (brace-list-open)
|
|
||||||
(brace-entry-open)
|
|
||||||
(substatement-open after)
|
|
||||||
(block-close . c-snug-do-while)
|
|
||||||
(arglist-cont-nonempty))
|
|
||||||
(c-cleanup-list brace-else-brace)
|
|
||||||
(c-offsets-alist
|
|
||||||
(statement-block-intro . +)
|
|
||||||
(substatement-open . 0)
|
|
||||||
(access-label . -)
|
|
||||||
(inline-open . 0)
|
|
||||||
(label . 0)
|
|
||||||
(statement-cont . +)))))
|
|
||||||
#+end_src
|
|
||||||
** Clang format
|
|
||||||
Clang format comes inbuilt with clang, so download that before using
|
|
||||||
this. Formats C/C++ files depending on a format (checkout the Clang
|
|
||||||
format [[file:~/Dotfiles/ClangFormat/.clang-format][config file]] in
|
|
||||||
my dotfiles).
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package clang-format
|
|
||||||
:straight nil
|
|
||||||
:load-path "/usr/share/clang/"
|
|
||||||
:after cc-mode
|
|
||||||
:commands (+code/clang-format-region-or-buffer
|
|
||||||
clang-format-mode)
|
|
||||||
;;; 2024-04-24: disabled as it's annoying on projects where a
|
|
||||||
;;; .clang-format isn't defined. Furthermore, does it make sense
|
|
||||||
;;; for *every* file you open and edit to have a format function run
|
|
||||||
;;; right after? seems a bit slow.
|
|
||||||
;; :hook
|
|
||||||
;; (c-mode-hook . clang-format-mode)
|
|
||||||
;; (c++-mode-hook . clang-format-mode)
|
|
||||||
:general
|
|
||||||
(code-leader
|
|
||||||
:keymaps '(c-mode-map c++-mode-map)
|
|
||||||
"f" #'+code/clang-format-region-or-buffer)
|
|
||||||
:config
|
|
||||||
(define-minor-mode clang-format-mode
|
|
||||||
"On save formats the current buffer via clang-format."
|
|
||||||
:lighter nil
|
|
||||||
(let ((save-func (proc (interactive)
|
|
||||||
(clang-format-buffer))))
|
|
||||||
(if clang-format-mode
|
|
||||||
(add-hook 'after-save-hook save-func nil t)
|
|
||||||
(remove-hook 'after-save-hook save-func t))))
|
|
||||||
(defun +code/clang-format-region-or-buffer ()
|
|
||||||
(interactive)
|
|
||||||
(if (mark)
|
|
||||||
(clang-format-region (region-beginning) (region-end))
|
|
||||||
(clang-format-buffer))))
|
|
||||||
#+end_src
|
|
||||||
** cc org babel
|
|
||||||
To ensure org-babel executes language blocks of C/C++, I need to load
|
|
||||||
it as an option in ~org-babel-load-languages~.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package org
|
|
||||||
:after cc-mode
|
|
||||||
:init
|
|
||||||
(org-babel-do-load-languages
|
|
||||||
'org-babel-load-languages
|
|
||||||
'((C . t))))
|
|
||||||
#+end_src
|
|
||||||
* WAIT D
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
D is a systems level programming language with C-style syntax. I
|
|
||||||
think it has some interesting ideas such as a toggleable garbage
|
|
||||||
collector. Here I just install the D-mode package, enable ~org-babel~
|
|
||||||
execution of d-mode blocks and alias ~D-mode~ with ~d-mode~.
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package d-mode
|
|
||||||
:defer t
|
|
||||||
:straight t
|
|
||||||
:config
|
|
||||||
(fset 'D-mode 'd-mode)
|
|
||||||
(with-eval-after-load "org-mode"
|
|
||||||
(setf (alist-get 'd org-babel-load-languages) t)))
|
|
||||||
#+end_src
|
|
||||||
* WAIT Rust
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package rust-mode
|
|
||||||
:straight t
|
|
||||||
:general
|
|
||||||
(code-leader
|
|
||||||
:keymaps 'rust-mode-map
|
|
||||||
"f" #'rust-format-buffer)
|
|
||||||
(local-leader
|
|
||||||
:keymaps 'rust-mode-map
|
|
||||||
"c" #'rust-run-clippy)
|
|
||||||
:init
|
|
||||||
(setq rust-format-on-save t)
|
|
||||||
(with-eval-after-load "eglot"
|
|
||||||
(add-to-list 'eglot-server-programs '(rust-mode "rust-analyzer"))))
|
|
||||||
#+end_src
|
|
||||||
* WAIT Racket
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
A scheme with lots of stuff inside it. Using it for a language design
|
|
||||||
book so it's useful to have some Emacs binds for it.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package racket-mode
|
|
||||||
:straight t
|
|
||||||
:hook (racket-mode-hook . racket-xp-mode)
|
|
||||||
:display
|
|
||||||
("\\*Racket.*"
|
|
||||||
(display-buffer-at-bottom)
|
|
||||||
(window-height . 0.25))
|
|
||||||
:init
|
|
||||||
(setq racket-documentation-search-location 'local)
|
|
||||||
:general
|
|
||||||
(nmap
|
|
||||||
:keymaps 'racket-describe-mode-map
|
|
||||||
"q" #'quit-window)
|
|
||||||
(nmap
|
|
||||||
:keymaps 'racket-mode-map
|
|
||||||
"gr" #'racket-eval-last-sexp)
|
|
||||||
(local-leader
|
|
||||||
:keymaps '(racket-mode-map racket-repl-mode-map)
|
|
||||||
"d" #'racket-repl-describe)
|
|
||||||
(local-leader
|
|
||||||
:keymaps 'racket-mode-map
|
|
||||||
"r" #'racket-run
|
|
||||||
"i" #'racket-repl
|
|
||||||
"e" #'racket-send-definition
|
|
||||||
"sr" #'racket-send-region
|
|
||||||
"sd" #'racket-send-definition))
|
|
||||||
#+end_src
|
|
||||||
* WAIT CSharp
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
Haven't used C# in a while, but Emacs is alright for it with
|
|
||||||
omnisharp.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package csharp-mode
|
|
||||||
:defer t
|
|
||||||
:pretty
|
|
||||||
(csharp-mode-hook
|
|
||||||
("null" . "∅")
|
|
||||||
("string" . "𝕊")
|
|
||||||
("List" . "ℓ")
|
|
||||||
("WriteLine" . "φ")
|
|
||||||
("Write" . "ω")
|
|
||||||
("true" . "⊨")
|
|
||||||
("false" . "⊭")
|
|
||||||
("!" . "¬")
|
|
||||||
("&&" . "∧")
|
|
||||||
("||" . "∨")
|
|
||||||
("for" . "∀")
|
|
||||||
("return" . "⟼")))
|
|
||||||
#+end_src
|
|
||||||
* WAIT Java
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
I kinda dislike Java, but if necessary I will code in it. Just setup
|
|
||||||
a style and some pretty symbols. You can use LSP to get cooler
|
|
||||||
features to be fair.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package ob-java
|
|
||||||
:straight nil
|
|
||||||
:defer t
|
|
||||||
:pretty
|
|
||||||
(java-mode-hook
|
|
||||||
("println" . "φ")
|
|
||||||
("printf" . "ω")
|
|
||||||
("null" . "Ø")
|
|
||||||
("true" . "⊨")
|
|
||||||
("false" . "⊭")
|
|
||||||
("!" . "¬")
|
|
||||||
("&&" . "∧")
|
|
||||||
("||" . "∨")
|
|
||||||
("for" . "∀")
|
|
||||||
("return" . "⟼"))
|
|
||||||
:config
|
|
||||||
(with-eval-after-load "cc-mode"
|
|
||||||
(c-add-style
|
|
||||||
"java"
|
|
||||||
'((c-basic-offset . 4)
|
|
||||||
(c-comment-only-line-offset 0 . 0)
|
|
||||||
(c-offsets-alist
|
|
||||||
(inline-open . 0)
|
|
||||||
(topmost-intro-cont . +)
|
|
||||||
(statement-block-intro . +)
|
|
||||||
(knr-argdecl-intro . 5)
|
|
||||||
(substatement-open . 0)
|
|
||||||
(substatement-label . +)
|
|
||||||
(label . +)
|
|
||||||
(statement-case-open . +)
|
|
||||||
(statement-cont . +)
|
|
||||||
(arglist-intro . c-lineup-arglist-intro-after-paren)
|
|
||||||
(arglist-close . c-lineup-arglist)
|
|
||||||
(brace-list-intro first c-lineup-2nd-brace-entry-in-arglist c-lineup-class-decl-init-+ +)
|
|
||||||
(access-label . 0)
|
|
||||||
(inher-cont . c-lineup-java-inher)
|
|
||||||
(func-decl-cont . c-lineup-java-throws))))
|
|
||||||
(add-to-list 'c-default-style '(java-mode . "java")))
|
|
||||||
|
|
||||||
(with-eval-after-load "abbrev"
|
|
||||||
(define-abbrev-table 'java-mode-abbrev-table nil)
|
|
||||||
(add-hook 'java-mode-hook
|
|
||||||
(proc (setq-local local-abbrev-table java-mode-abbrev-table)))))
|
|
||||||
#+end_src
|
|
||||||
* Haskell
|
|
||||||
Haskell is a static lazy functional programming language (what a
|
|
||||||
mouthful). It's quite a beautiful language and really learning it will
|
|
||||||
change the way you think about programming. However, my preferred
|
|
||||||
functional language is still unfortunately Lisp so no extra brownie
|
|
||||||
points there.
|
|
||||||
|
|
||||||
Here I configure the REPL for Haskell via the
|
|
||||||
~haskell-interactive-mode~. I also load my custom package
|
|
||||||
[[file:elisp/haskell-multiedit.el][haskell-multiedit]] which allows a
|
|
||||||
user to create temporary ~haskell-mode~ buffers that, upon completion,
|
|
||||||
will run in the REPL. Even easier than making your own buffer.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package haskell-mode
|
|
||||||
:hook
|
|
||||||
(haskell-mode-hook . haskell-indentation-mode)
|
|
||||||
(haskell-mode-hook . interactive-haskell-mode)
|
|
||||||
:custom
|
|
||||||
(haskell-interactive-prompt "[λ] ")
|
|
||||||
(haskell-interactive-prompt-cont "{λ} ")
|
|
||||||
(haskell-interactive-popup-errors nil)
|
|
||||||
(haskell-stylish-on-save nil)
|
|
||||||
(haskell-process-type 'auto)
|
|
||||||
:general
|
|
||||||
(shell-leader
|
|
||||||
"h" #'haskell-interactive-bring)
|
|
||||||
(local-leader
|
|
||||||
:keymaps 'haskell-mode-map
|
|
||||||
"l" #'haskell-process-load-or-reload
|
|
||||||
"t" #'haskell-process-do-type)
|
|
||||||
(local-leader
|
|
||||||
:keymaps 'haskell-interactive-mode-map
|
|
||||||
"c" #'haskell-interactive-mode-clear)
|
|
||||||
(imap
|
|
||||||
:keymaps 'haskell-interactive-mode-map
|
|
||||||
"M-k" #'haskell-interactive-mode-history-previous
|
|
||||||
"M-j" #'haskell-interactive-mode-history-next)
|
|
||||||
:display
|
|
||||||
("\\*haskell.**\\*"
|
|
||||||
(display-buffer-at-bottom)
|
|
||||||
(window-height . 0.25))
|
|
||||||
:config
|
|
||||||
(load (concat user-emacs-directory "elisp/haskell-multiedit.el")))
|
|
||||||
#+end_src
|
|
||||||
* Python
|
|
||||||
Works well for python. If you have ~pyls~ it should be on your path, so
|
|
||||||
just run eglot if you need. But an LSP server is not necessary for a
|
|
||||||
lot of my time in python. Here I also setup org-babel for python
|
|
||||||
source code blocks.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package python
|
|
||||||
:defer t
|
|
||||||
:straight nil
|
|
||||||
:pretty
|
|
||||||
(python-mode-hook
|
|
||||||
("None" . "Ø")
|
|
||||||
("list" . "ℓ")
|
|
||||||
("List" . "ℓ")
|
|
||||||
("str" . "𝕊")
|
|
||||||
("True" . "⊨")
|
|
||||||
("False" . "⊭")
|
|
||||||
("!" . "¬")
|
|
||||||
("&&" . "∧")
|
|
||||||
("||" . "∨")
|
|
||||||
("for" . "∀")
|
|
||||||
("print" . "φ")
|
|
||||||
("lambda" . "λ")
|
|
||||||
("return" . "⟼")
|
|
||||||
("yield" . "⟻"))
|
|
||||||
:init
|
|
||||||
(setq python-indent-offset 4)
|
|
||||||
:config
|
|
||||||
(with-eval-after-load "org-mode"
|
|
||||||
(setf (alist-get 'python org-babel-load-languages) t)))
|
|
||||||
#+end_src
|
|
||||||
** Python shell
|
|
||||||
Setup for python shell, including a toggle option
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package python
|
|
||||||
:straight nil
|
|
||||||
:commands +python/toggle-repl
|
|
||||||
:general
|
|
||||||
(shell-leader
|
|
||||||
"p" #'run-python)
|
|
||||||
:display
|
|
||||||
("\\*Python\\*"
|
|
||||||
(display-buffer-at-bottom)
|
|
||||||
(window-height . 0.25)))
|
|
||||||
#+end_src
|
|
||||||
* YAML
|
|
||||||
YAML is a data language which is useful for config files.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package yaml-mode
|
|
||||||
:straight t)
|
|
||||||
#+end_src
|
|
||||||
* HTML/CSS/JS
|
|
||||||
Firstly, web mode for consistent colouring of syntax.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package web-mode
|
|
||||||
:mode ("\\.html" . web-mode)
|
|
||||||
:mode ("\\.js" . web-mode)
|
|
||||||
:mode ("\\.css" . web-mode)
|
|
||||||
:custom
|
|
||||||
((web-mode-code-indent-offset 2)
|
|
||||||
(web-mode-markup-indent-offset 2)
|
|
||||||
(web-mode-css-indent-offset 2)))
|
|
||||||
#+end_src
|
|
||||||
** Emmet
|
|
||||||
Emmet for super speed code writing.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package emmet-mode
|
|
||||||
:hook (web-mode-hook . emmet-mode)
|
|
||||||
:general
|
|
||||||
(imap
|
|
||||||
:keymaps 'emmet-mode-keymap
|
|
||||||
"TAB" #'emmet-expand-line
|
|
||||||
"M-j" #'emmet-next-edit-point
|
|
||||||
"M-k" #'emmet-prev-edit-point))
|
|
||||||
#+end_src
|
|
||||||
** HTML Auto insert
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package web-mode
|
|
||||||
:defer t
|
|
||||||
:auto-insert
|
|
||||||
(("\\.html\\'" . "HTML Skeleton")
|
|
||||||
""
|
|
||||||
"<!doctype html>
|
|
||||||
<html lang=''>
|
|
||||||
<head>
|
|
||||||
<meta charset='utf-8'>
|
|
||||||
<meta http-equiv='x-ua-compatible' content='ie=edge'>
|
|
||||||
<title>"(read-string "Enter title: ") | """</title>
|
|
||||||
<meta name='description' content='" (read-string "Enter description: ") | "" "'>
|
|
||||||
<meta name='author' content='"user-full-name"'/>
|
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
|
||||||
|
|
||||||
<link rel='apple-touch-icon' href='/apple-touch-icon.png'>
|
|
||||||
<link rel='shortcut icon' href='/favicon.ico'/>
|
|
||||||
<!-- Place favicon.ico in the root directory -->
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!--[if lt IE 8]>
|
|
||||||
<p class='browserupgrade'>
|
|
||||||
You are using an <strong>outdated</strong> browser. Please
|
|
||||||
<a href='http://browsehappy.com/'>upgrade your browser</a> to improve
|
|
||||||
your experience.
|
|
||||||
</p>
|
|
||||||
<![endif]-->
|
|
||||||
"
|
|
||||||
_
|
|
||||||
" </body>
|
|
||||||
</html>"))
|
|
||||||
#+end_src
|
|
||||||
** WAIT Typescript
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
A child language of javascript which compiles to it.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package typescript-mode
|
|
||||||
:defer t
|
|
||||||
:init
|
|
||||||
(setq typescript-indent-level 2))
|
|
||||||
#+end_src
|
|
||||||
* Common Lisp
|
|
||||||
Common Lisp is a dialect of Lisp, the most /common/ one around. Emacs
|
|
||||||
comes with builtin Lisp support of course, but a REPL would be nice.
|
|
||||||
|
|
||||||
** WAIT Sly
|
|
||||||
Enter /SLY/. Sly is a fork of /SLIME/ and is *mandatory* for lisp
|
|
||||||
development on Emacs.
|
|
||||||
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package sly
|
|
||||||
:straight t
|
|
||||||
:init
|
|
||||||
(setq inferior-lisp-program "sbcl")
|
|
||||||
:display
|
|
||||||
("\\*sly-db"
|
|
||||||
(display-buffer-at-bottom)
|
|
||||||
(window-height . 0.5))
|
|
||||||
("\\*sly-"
|
|
||||||
(display-buffer-at-bottom)
|
|
||||||
(window-height . 0.25))
|
|
||||||
:config
|
|
||||||
(evil-set-initial-state 'sly-db-mode 'emacs)
|
|
||||||
(with-eval-after-load "org"
|
|
||||||
(setq-default org-babel-lisp-eval-fn #'sly-eval))
|
|
||||||
(with-eval-after-load "company"
|
|
||||||
(add-hook 'sly-mrepl-hook #'company-mode))
|
|
||||||
:general
|
|
||||||
(shell-leader
|
|
||||||
"s" #'sly-mrepl)
|
|
||||||
(nmap
|
|
||||||
:keymaps '(lisp-mode-map sly-mrepl-mode-map)
|
|
||||||
"gr" #'sly-eval-buffer
|
|
||||||
"gd" #'sly-edit-definition
|
|
||||||
"gR" #'sly-who-calls)
|
|
||||||
(local-leader
|
|
||||||
:keymaps '(lisp-mode-map sly-mrepl-mode-map)
|
|
||||||
"s" #'+shell/toggle-sly
|
|
||||||
"c" #'sly-compile-file
|
|
||||||
"a" #'sly-apropos
|
|
||||||
"d" #'sly-describe-symbol
|
|
||||||
"D" #'sly-documentation-lookup
|
|
||||||
"S" #'sly-mrepl-sync
|
|
||||||
"E" #'sly-eval-defun)
|
|
||||||
(local-leader
|
|
||||||
:keymaps 'lisp-mode-map
|
|
||||||
:infix "e"
|
|
||||||
"b" #'sly-eval-buffer
|
|
||||||
"e" #'sly-eval-last-expression
|
|
||||||
"f" #'sly-eval-defun
|
|
||||||
"r" #'sly-eval-region)
|
|
||||||
(nmap
|
|
||||||
:keymaps 'sly-inspector-mode-map
|
|
||||||
"q" #'sly-inspector-quit))
|
|
||||||
#+end_src
|
|
||||||
** Emacs lisp
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package elisp-mode
|
|
||||||
:straight nil
|
|
||||||
:pretty
|
|
||||||
(lisp-mode-hook
|
|
||||||
("lambda" . "λ")
|
|
||||||
("t" . "⊨")
|
|
||||||
("nil" . "Ø")
|
|
||||||
("and" . "∧")
|
|
||||||
("or" . "∨")
|
|
||||||
("defun" . "ƒ")
|
|
||||||
("for" . "∀")
|
|
||||||
("mapc" . "∀")
|
|
||||||
("mapcar" . "∀"))
|
|
||||||
(emacs-lisp-mode-hook
|
|
||||||
("lambda" . "λ")
|
|
||||||
("t" . "⊨")
|
|
||||||
("nil" . "Ø")
|
|
||||||
("and" . "∧")
|
|
||||||
("or" . "∨")
|
|
||||||
("defun" . "ƒ")
|
|
||||||
("for" . "∀")
|
|
||||||
("mapc" . "∀")
|
|
||||||
("mapcar" . "∀"))
|
|
||||||
:general
|
|
||||||
(:states '(normal motion visual)
|
|
||||||
:keymaps '(emacs-lisp-mode-map lisp-mode-map)
|
|
||||||
")" #'sp-next-sexp
|
|
||||||
"(" #'sp-previous-sexp)
|
|
||||||
(nmmap
|
|
||||||
:keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map)
|
|
||||||
"gr" #'eval-last-sexp)
|
|
||||||
(vmap
|
|
||||||
:keymaps '(emacs-lisp-mode-map lisp-interaction-mode-map)
|
|
||||||
"gr" #'eval-region))
|
|
||||||
#+end_src
|
|
||||||
** WIP Hydra like Lispy
|
|
||||||
:PROPERTIES:
|
|
||||||
:header-args:emacs-lisp: :tangle no
|
|
||||||
:END:
|
|
||||||
A [[file:core.org::*Hydra][Hydra]] which uses the ~Lispy~ package (by
|
|
||||||
abo-abo) to create a set of motions that allow movement around a lisp
|
|
||||||
file easily.
|
|
||||||
|
|
||||||
2024-04-18: Still working on this, quite rough around the edges.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package lispy
|
|
||||||
:after (lisp-mode elisp-mode)
|
|
||||||
:hydra
|
|
||||||
(hydra-lispy
|
|
||||||
nil "Move around quickly in Lisp"
|
|
||||||
("h" #'lispy-left)
|
|
||||||
("j" ("t" #'lispy-teleport)
|
|
||||||
#'lispy-down)
|
|
||||||
("k" #'lispy-up)
|
|
||||||
("l" #'lispy-right)
|
|
||||||
("d" #'lispy-different)
|
|
||||||
("u" #'lispy-flow)
|
|
||||||
("o" #'lispy-oneline)
|
|
||||||
("m" #'lispy-multiline)
|
|
||||||
("N" #'lispy-narrow)
|
|
||||||
("W" #'lispy-widen)
|
|
||||||
("c" #'lispy-clone)
|
|
||||||
("fp" #'lispy-ace-paren)
|
|
||||||
("fs" #'lispy-ace-symbol :exit t)
|
|
||||||
("H" #'lispy-slurp)
|
|
||||||
("L" #'lispy-barf)
|
|
||||||
("M-h" #'lispy-move-left)
|
|
||||||
("M-j" #'lispy-move-down)
|
|
||||||
("M-k" #'lispy-move-up)
|
|
||||||
("M-l" #'lispy-move-right)
|
|
||||||
("C-g" nil))
|
|
||||||
:general
|
|
||||||
(nmmap
|
|
||||||
:keymaps '(emacs-lisp-mode-map lisp-mode-map)
|
|
||||||
"." #'hydra-lispy/body))
|
|
||||||
#+end_src
|
|
||||||
** Lisp indent function
|
|
||||||
Add a new lisp indent function which indents newline lists more
|
|
||||||
appropriately.
|
|
||||||
#+begin_src emacs-lisp
|
|
||||||
(use-package lisp-mode
|
|
||||||
:straight nil
|
|
||||||
:config
|
|
||||||
(defun +oreo/lisp-indent-function (indent-point state)
|
|
||||||
(let ((normal-indent (current-column))
|
|
||||||
(orig-point (point)))
|
|
||||||
(goto-char (1+ (elt state 1)))
|
|
||||||
(parse-partial-sexp (point) calculate-lisp-indent-last-sexp 0 t)
|
|
||||||
(cond
|
|
||||||
;; car of form doesn't seem to be a symbol, or is a keyword
|
|
||||||
((and (elt state 2)
|
|
||||||
(or (not (looking-at "\\sw\\|\\s_"))
|
|
||||||
(looking-at ":")))
|
|
||||||
(if (not (> (save-excursion (forward-line 1) (point))
|
|
||||||
calculate-lisp-indent-last-sexp))
|
|
||||||
(progn (goto-char calculate-lisp-indent-last-sexp)
|
|
||||||
(beginning-of-line)
|
|
||||||
(parse-partial-sexp (point)
|
|
||||||
calculate-lisp-indent-last-sexp 0 t)))
|
|
||||||
;; Indent under the list or under the first sexp on the same
|
|
||||||
;; line as calculate-lisp-indent-last-sexp. Note that first
|
|
||||||
;; thing on that line has to be complete sexp since we are
|
|
||||||
;; inside the innermost containing sexp.
|
|
||||||
(backward-prefix-chars)
|
|
||||||
(current-column))
|
|
||||||
((and (save-excursion
|
|
||||||
(goto-char indent-point)
|
|
||||||
(skip-syntax-forward " ")
|
|
||||||
(not (looking-at ":")))
|
|
||||||
(save-excursion
|
|
||||||
(goto-char orig-point)
|
|
||||||
(looking-at ":")))
|
|
||||||
(save-excursion
|
|
||||||
(goto-char (+ 2 (elt state 1)))
|
|
||||||
(current-column)))
|
|
||||||
(t
|
|
||||||
(let ((function (buffer-substring (point)
|
|
||||||
(progn (forward-sexp 1) (point))))
|
|
||||||
method)
|
|
||||||
(setq method (or (function-get (intern-soft function)
|
|
||||||
'lisp-indent-function)
|
|
||||||
(get (intern-soft function) 'lisp-indent-hook)))
|
|
||||||
(cond ((or (eq method 'defun)
|
|
||||||
(and (null method)
|
|
||||||
(> (length function) 3)
|
|
||||||
(string-match "\\`def" function)))
|
|
||||||
(lisp-indent-defform state indent-point))
|
|
||||||
((integerp method)
|
|
||||||
(lisp-indent-specform method state
|
|
||||||
indent-point normal-indent))
|
|
||||||
(method
|
|
||||||
(funcall method indent-point state))))))))
|
|
||||||
(setq-default lisp-indent-function #'+oreo/lisp-indent-function))
|
|
||||||
#+end_src
|
|
||||||
Reference in New Issue
Block a user