(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
|
||||
(defconst +literate/org-files
|
||||
(+literate/filter
|
||||
#'+literate/org-p
|
||||
(mapcar #'(lambda (file) (concat user-emacs-directory file))
|
||||
(cddr (directory-files user-emacs-directory)))))
|
||||
(mapcar #'(lambda (x) (expand-file-name (concat user-emacs-directory x)))
|
||||
(list "config.org")))
|
||||
|
||||
(defconst +literate/el-init-files
|
||||
`(,(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