3774 lines
116 KiB
Org Mode
3774 lines
116 KiB
Org Mode
#+title: Emacs configuration
|
||
#+author: Aryadev Chavali
|
||
#+description: My Emacs configuration
|
||
#+property: header-args:emacs-lisp :tangle config.el :comments link :results none
|
||
#+startup: noindent
|
||
#+options: toc:t num:t
|
||
#+latex_header:\usepackage[margin=1.0in]{geometry}
|
||
#+latex_class: article
|
||
#+latex_class_options: [a4paper,12pt]
|
||
|
||
* Introduction
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle config.el :results none
|
||
:END:
|
||
Welcome to my Emacs configuration. You may be confused by the fact
|
||
it's a readable document rather than some code; this file serves as
|
||
both documentation *and* code. Here's an example of some Emacs Lisp
|
||
code:
|
||
#+begin_src emacs-lisp
|
||
;;; config.el --- Compiled configuration from config.org -*- lexical-binding: t; -*-
|
||
|
||
;; Copyright (C) 2024 Aryadev Chavali
|
||
|
||
;; Author: Aryadev Chavali <aryadev@aryadevchavali.com>
|
||
|
||
;; You may distribute and modify this code under the terms of the MIT
|
||
;; license. You should have received a copy of the MIT license with
|
||
;; this file. If not, please write to: aryadev@aryadevchavali.com.
|
||
|
||
;;; Commentary:
|
||
;; Welcome to my Emacs configuration. This file is considered
|
||
;; volatile i.e. any edits made to this file will be overwritten if
|
||
;; and when the configuration is compiled again.
|
||
|
||
;; To propagate edits from this file to the literate document, call
|
||
;; (org-babel-detangle).
|
||
;;; Code:
|
||
#+end_src
|
||
|
||
This is an Emacs Lisp code block, something you will see a *LOT* of
|
||
throughout. All the Emacs Lisp code blocks in this document are
|
||
collected, concatenated together then fed into a file (=config.el=).
|
||
This code file is then evaluated by Emacs
|
||
[[file:init.el::+literate/load-config][at boot]].
|
||
|
||
This style of coding is called /literate programming/. Donald Knuth
|
||
[[https://en.wikipedia.org/wiki/Literate_programming][really liked]]
|
||
the idea. I mainly utilise this to explain my decisions for
|
||
configuring or using certain packages: Emacs is an opinionated piece
|
||
of software after all.
|
||
|
||
Sections tagged =WAIT= are not compiled and are, hence, unused.
|
||
Usually I provide some reasoning as to why. A lot of code here is
|
||
essentially write and forget; nothing needs to change unless I find a
|
||
more efficient way to do things.
|
||
|
||
Some sections border on blog posts justifying why I think they're good
|
||
applications or giving some greater reasoning about my specific
|
||
configuration of a package. That can be distracting, so tangling this
|
||
file (using ~(org-babel-tangle)~) and looking at the source code may
|
||
be more helpful.
|
||
* Basics
|
||
Let's setup a few things:
|
||
+ My name and mail address
|
||
+ File encoding
|
||
+ Backup files (~backup-directory-alist~)
|
||
+ Refreshing buffers when a change occurs (~auto-revert-mode~)
|
||
+ Yes or no questions being less painful (~y-or-n-p~)
|
||
+ Make the clipboard work seamlessly with the clipboard
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package emacs
|
||
:demand t
|
||
:init
|
||
(setq user-full-name "Aryadev Chavali"
|
||
user-mail-address "aryadev@aryadevchavali.com"
|
||
buffer-file-coding-system 'utf-8-unix
|
||
save-buffer-coding-system 'utf-8-unix
|
||
backup-directory-alist `(("." . ,(no-littering-expand-var-file-name "saves/")))
|
||
global-auto-revert-non-file-buffers t
|
||
auto-revert-verbose nil
|
||
select-enable-clipboard t)
|
||
:config
|
||
(fset 'yes-or-no-p 'y-or-n-p)
|
||
(global-auto-revert-mode))
|
||
#+end_src
|
||
* Custom functionality
|
||
This is custom Lisp I've written to help me out throughout the
|
||
configuration. Note that because it's setup so early
|
||
** Procedure
|
||
A ~lambda~ which takes no arguments is a procedure. This macro
|
||
generates procedures, with the parameters of the macro being the body
|
||
of the procedure. It returns it in quoted form, as that is the most
|
||
common use of this macro.
|
||
|
||
#+begin_src emacs-lisp
|
||
(defmacro proc (&rest BODY)
|
||
"For a given list of forms BODY, return a quoted 0 argument
|
||
lambda."
|
||
`(quote (lambda nil ,@BODY)))
|
||
#+end_src
|
||
** Automatically run a command on saving
|
||
Define a macro which creates hooks into the ~after-save-hook~. On
|
||
certain ~conditions~ being met, ~to-run~ is evaluated.
|
||
#+begin_src emacs-lisp
|
||
(use-package simple
|
||
:defer t
|
||
:config
|
||
(defmacro +oreo/create-auto-save (conditions &rest to-run)
|
||
"Create a hook for after saves, where on CONDITIONS being met
|
||
TO-RUN is evaluated."
|
||
`(add-hook 'after-save-hook
|
||
(proc
|
||
(interactive)
|
||
(when ,conditions ,@to-run)))))
|
||
#+end_src
|
||
** System specificity
|
||
A macro that acts as a switch case on ~(system-name)~ so a user can
|
||
write code for multiple hosts. For me this is for my desktop and
|
||
laptop. Though there may be an easier solution than this, this seems
|
||
simple enough.
|
||
|
||
Note the check for the symbol ~otherwise~ which acts as the default
|
||
case.
|
||
#+begin_src emacs-lisp
|
||
(defmacro +oreo/sys-name-cond (&rest pairs)
|
||
"Switch case on result of function `system-name'.
|
||
|
||
Each pair in PAIRS is typed as: (string . (forms...)) where the
|
||
string represents the system name to test, and forms being the
|
||
consequence if true. if string is the symbol OTHERWISE, then it
|
||
is considered the default case."
|
||
`(cond ,@(mapcar
|
||
#'(lambda (pair)
|
||
(cl-destructuring-bind (name . body) pair
|
||
(if (eq name 'otherwise)
|
||
`(t ,@body)
|
||
`((string= (system-name) ,name) ,@body))))
|
||
pairs)))
|
||
#+end_src
|
||
*** Setting number of native jobs
|
||
In [[file:early-init.el][early-init.el]] I set the number of
|
||
native-workers to 4, which isn't necessarily optimal when
|
||
loading/compiling the rest of this file depending on the machine I
|
||
use:
|
||
- On my laptop (=newboy=) I'd prefer to have it use 2-3 threads so
|
||
I can actually use the rest of the laptop while waiting for
|
||
compilation
|
||
- On my desktop (=oldboy=) I'd prefer to use 4-6 threads as I can
|
||
afford more, so I can get a faster load up.
|
||
#+begin_src emacs-lisp
|
||
(use-package comp
|
||
:init
|
||
(+oreo/sys-name-cond
|
||
("newboy"
|
||
(setq native-comp-async-jobs-number 3))
|
||
("oldboy"
|
||
(setq native-comp-async-jobs-number 6))))
|
||
#+end_src
|
||
** Clean buffer list
|
||
Clean all buffers except for those in ~+oreo/keep-buffers~.
|
||
#+begin_src emacs-lisp
|
||
(defconst +oreo/keep-buffers
|
||
(list "config.org" "*scratch*"
|
||
"*dashboard*" "*Messages*"
|
||
"*Warnings*" "*eshell*")
|
||
"List of buffer names to preserve.")
|
||
|
||
(defun +oreo/clean-buffers ()
|
||
"Kill all buffers except any with names in +oreo/keep-buffers."
|
||
(interactive)
|
||
(let ((should-not-kill #'(lambda (buf) (member (buffer-name buf) +oreo/keep-buffers)))
|
||
(buffers (buffer-list)))
|
||
(mapcar #'kill-buffer (cl-remove-if should-not-kill buffers))))
|
||
#+end_src
|
||
* Aesthetics
|
||
General look and feel of Emacs (mostly disabling stuff I don't like).
|
||
** Themes
|
||
I have both a dark and light theme for differing situations.
|
||
*** Dark theme
|
||
My preferred dark theme is my own "personal-primary" theme which is
|
||
stored in the Emacs lisp folder (look at
|
||
[[file:elisp/personal-primary-theme.el][this file]]). It tries to use
|
||
the primary colours for everything, which makes it quite simple to
|
||
look at.
|
||
|
||
I have an older version of this theme that uses a homogeneous colour
|
||
scheme ([[file:elisp/personal-theme.el][this file]])
|
||
#+begin_src emacs-lisp
|
||
(use-package custom
|
||
:demand t
|
||
:init
|
||
(setq custom-theme-directory (concat user-emacs-directory "elisp/"))
|
||
:config
|
||
(load-theme 'personal-primary t))
|
||
#+end_src
|
||
*** Switching between light and dark
|
||
I'm not very good at designing light themes as I don't really use
|
||
them. However they are necessary in high light situations where a
|
||
dark mode would strain the eyes too much. So I built a custom theme
|
||
on top of the default Emacs theme, "personal-light".
|
||
|
||
2024-06-11: I now use modus-operandi for my light theme.
|
||
|
||
I don't use it by default but I may need to switch between light and
|
||
dark easily, so here's a command to switch between them.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package custom
|
||
:defer t
|
||
:commands +oreo/switch-theme
|
||
:init
|
||
(defvar +oreo/theme 'dark)
|
||
:config
|
||
(defun +oreo/switch-theme ()
|
||
(interactive)
|
||
(cl-case +oreo/theme
|
||
(dark
|
||
(mapc #'disable-theme custom-enabled-themes)
|
||
(load-theme 'modus-operandi t)
|
||
(setq +oreo/theme 'light))
|
||
(light
|
||
(mapc #'disable-theme custom-enabled-themes)
|
||
(load-theme 'personal-primary t)
|
||
(setq +oreo/theme 'dark)))))
|
||
#+end_src
|
||
** Font size
|
||
Make font size bigger on my laptop and smaller on my desktop.
|
||
#+begin_src emacs-lisp
|
||
(use-package faces
|
||
:defer t
|
||
:config
|
||
(+oreo/sys-name-cond
|
||
("newboy" (set-face-attribute 'default nil :height 145))
|
||
("oldboy" (set-face-attribute 'default nil :height 135))))
|
||
#+end_src
|
||
** Startup screen
|
||
The default startup screen is quite bad in all honesty. While for a
|
||
first time user it can be very helpful in running the tutorial and
|
||
finding out more about Emacs, for someone who's already configured it
|
||
there isn't much point.
|
||
|
||
The scratch buffer is an interaction buffer, made when Emacs is first
|
||
started, to quickly prototype Emacs Lisp code. When startup screen is
|
||
disabled, this buffer is the first thing presented on boot for Emacs.
|
||
So we can use it to store some useful information.
|
||
|
||
2024-06-04: I use to load [[*Org mode][org-mode]] here for the scratch
|
||
buffer and it literally added 2 seconds of load time, so let's just
|
||
use fundamental mode and call it a day.
|
||
#+begin_src emacs-lisp
|
||
(use-package emacs
|
||
:defer t
|
||
:init
|
||
(setq inhibit-startup-screen t
|
||
inhibit-startup-echo-area-message user-login-name
|
||
initial-major-mode 'fundamental-mode
|
||
initial-scratch-message ""
|
||
ring-bell-function 'ignore)
|
||
:config
|
||
(add-hook
|
||
'emacs-startup-hook
|
||
(proc
|
||
(with-current-buffer "*scratch*"
|
||
(goto-char (point-max))
|
||
(insert
|
||
(format
|
||
"Emacs v%s - %s\n"
|
||
emacs-version (emacs-init-time)))))))
|
||
#+end_src
|
||
** Blinking cursor
|
||
Turn on blinking cursor.
|
||
|
||
2021-03-15: Turn off blinking-cursor-mode as [[*Hl-line][hl-line]] is better.
|
||
2024-06-04: Actually a blinking cursor helps to see if Emacs is
|
||
hanging, which hl-line just can't do.
|
||
#+begin_src emacs-lisp
|
||
(use-package frame
|
||
:defer t
|
||
:init
|
||
(setq blink-cursor-delay 0.2)
|
||
:config
|
||
(blink-cursor-mode))
|
||
#+end_src
|
||
** Fringes
|
||
Turning off borders in my window manager was a good idea, so turn off
|
||
the borders for Emacs.
|
||
#+begin_src emacs-lisp
|
||
(use-package fringe
|
||
:defer t
|
||
:config
|
||
(fringe-mode 0))
|
||
#+end_src
|
||
** Mode line
|
||
A mode line in an editor can provide a LOT of information, or very
|
||
little. I customised the Emacs modeline to give me a bit of info,
|
||
~telephone-line~ to give me a lot.
|
||
|
||
Currently I use the default mode line with some customisation;
|
||
simplicity is above all.
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq-default
|
||
mode-line-format
|
||
'("%l:%c " ;; Line and column
|
||
"%p[" ;; %into file
|
||
(:eval (with-eval-after-load "evil" ;; Evil state
|
||
(upcase
|
||
(substring
|
||
(format "%s" (if (bound-and-true-p evil-state)
|
||
evil-state
|
||
" "))
|
||
0 1))))
|
||
"] "
|
||
"%+"
|
||
"%b"
|
||
"(" ;; Buffer name
|
||
(:eval (format "%s" major-mode))
|
||
") "
|
||
"%I " ;; file size
|
||
(:eval (if (project-current)
|
||
(project-name (project-current))))
|
||
(vc-mode vc-mode) ;; git branch
|
||
" "
|
||
(:eval
|
||
(with-eval-after-load "eglot"
|
||
(if eglot--managed-mode
|
||
(eglot--mode-line-format))))
|
||
mode-line-misc-info
|
||
mode-line-end-spaces))
|
||
#+end_src
|
||
** Mouse
|
||
Who uses a mouse? 🤮
|
||
|
||
This disables the use of GUI dialogues for stuff.
|
||
#+begin_src emacs-lisp
|
||
(setq-default use-file-dialog nil
|
||
use-dialog-box nil)
|
||
#+end_src
|
||
** Scrolling
|
||
Emacs can automatically scroll the buffer depending on how many lines
|
||
the cursor is away from the limits of the window. Here I set the
|
||
margin to 8 (so it'll start correcting at 8) and scroll-conservatively
|
||
to the same value so it'll keep the cursor centred.
|
||
#+begin_src emacs-lisp
|
||
(use-package emacs
|
||
:init
|
||
(setq scroll-conservatively 8
|
||
scroll-margin 8))
|
||
#+end_src
|
||
* Core packages
|
||
For my core packages, whose configuration doesn't change much anyway,
|
||
** 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
|
||
:after general
|
||
: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-window
|
||
"t" #'find-file-other-tab
|
||
"s" #'save-buffer)
|
||
|
||
(buffer-leader
|
||
"b" #'switch-to-buffer
|
||
"d" #'kill-current-buffer
|
||
"c" #'kill-buffer-and-window
|
||
"K" #'kill-buffer
|
||
"j" #'next-buffer
|
||
"k" #'previous-buffer
|
||
"D" '(+oreo/clean-buffers :which-key "Kill most buffers"))
|
||
|
||
(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"
|
||
+ Swapping any two textual "objects" is such a Vim thing (the verb
|
||
object model) but by default it can't seem to do it. But Emacs
|
||
can...
|
||
#+begin_src emacs-lisp
|
||
(use-package evil
|
||
:demand t
|
||
:straight t
|
||
: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)
|
||
|
||
(general-def
|
||
:keymaps 'override
|
||
:states '(normal motion visual)
|
||
"gu" #'evil-upcase
|
||
"gU" #'evil-downcase
|
||
"M-y" #'yank-pop
|
||
"T" nil)
|
||
|
||
(general-def
|
||
:keymaps 'override
|
||
:states '(normal motion visual)
|
||
:infix "T"
|
||
"w" #'transpose-words
|
||
"c" #'transpose-chars
|
||
"s" #'transpose-sentences
|
||
"p" #'transpose-paragraphs
|
||
"e" #'transpose-sexps
|
||
"l" #'transpose-lines)
|
||
:init
|
||
(setq evil-want-keybinding nil
|
||
evil-split-window-below t
|
||
evil-vsplit-window-right t
|
||
evil-want-abbrev-expand-on-insert-exit t
|
||
evil-undo-system #'undo-tree)
|
||
:config
|
||
(evil-mode))
|
||
#+end_src
|
||
*** Evil surround
|
||
Evil surround is a port for vim-surround.
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-surround
|
||
:after evil
|
||
:straight t
|
||
: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
|
||
:straight t
|
||
: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
|
||
:straight t
|
||
: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
|
||
(global-evil-mc-mode))
|
||
;; (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)
|
||
#+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
|
||
:straight t
|
||
: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
|
||
:straight t
|
||
:defer t
|
||
: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
|
||
:demand t
|
||
:straight t
|
||
: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))
|
||
(setq ivy-height 8
|
||
ivy-height-alist nil
|
||
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
|
||
(ivy-mode 1)
|
||
(require 'counsel nil t))
|
||
#+end_src
|
||
**** Counsel
|
||
Setup for counsel. Load after ivy and helpful.
|
||
#+begin_src emacs-lisp
|
||
(use-package counsel
|
||
:straight t
|
||
:defer t
|
||
:general
|
||
(search-leader
|
||
"s" #'counsel-grep-or-swiper
|
||
"R" #'counsel-rg)
|
||
(file-leader
|
||
"r" #'counsel-recentf)
|
||
(insert-leader
|
||
"c" #'counsel-unicode-char)
|
||
(general-def
|
||
[remap describe-bindings] #'counsel-descbinds
|
||
[remap load-theme] #'counsel-load-theme)
|
||
:config
|
||
(setq ivy-initial-inputs-alist '((org-insert-link . "^"))
|
||
counsel-describe-function-function #'helpful-callable
|
||
counsel-describe-variable-function #'helpful-variable
|
||
counsel-grep-swiper-limit 1500000
|
||
ivy-re-builders-alist '((swiper . ivy--regex-plus)
|
||
(counsel-grep-or-swiper . ivy--regex-plus)
|
||
(counsel-rg . ivy--regex-plus)
|
||
(t . ivy--regex-ignore-order)))
|
||
(counsel-mode 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
|
||
:straight t
|
||
:defer 2
|
||
:init
|
||
(setq amx-backend 'ivy)
|
||
: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
|
||
:straight t
|
||
: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
|
||
:defer t
|
||
: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"
|
||
(evil-set-initial-state 'completions-list-mode 'normal)))
|
||
#+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
|
||
:defer t
|
||
: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
|
||
:demand t
|
||
: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
|
||
:demand t
|
||
:general
|
||
: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
|
||
: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
|
||
:init
|
||
(setq tab-bar-show 1)
|
||
:config
|
||
(tab-bar-mode)
|
||
:general
|
||
(tab-leader
|
||
"R" #'tab-rename
|
||
"c" #'tab-close
|
||
"d" #'tab-close
|
||
"f" #'tab-detach
|
||
"h" #'tab-move-to
|
||
"j" #'tab-next
|
||
"k" #'tab-previous
|
||
"l" #'tab-move
|
||
"n" #'tab-new
|
||
"r" #'tab-switch
|
||
"w" #'tab-window-detach)
|
||
(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
|
||
:defer t
|
||
: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
|
||
*** 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
|
||
:demand t
|
||
:hook (emacs-startup-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
|
||
Look at the snippets [[file:../.config/yasnippet/snippets/][folder]]
|
||
for all snippets I've got.
|
||
#+begin_src emacs-lisp
|
||
(use-package yasnippet
|
||
:straight t
|
||
: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
|
||
*** Hydra
|
||
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)
|
||
|
||
(use-package hydra
|
||
:hydra
|
||
(hydra-window-resize
|
||
nil "Resize the current window effectively"
|
||
(">" #'evil-window-increase-width)
|
||
("<" #'evil-window-decrease-width)
|
||
("-" #'evil-window-decrease-height)
|
||
("+" #'evil-window-increase-height)
|
||
("=" #'balance-windows))
|
||
:general
|
||
(leader
|
||
"wr" #'hydra-window-resize/body))
|
||
#+end_src
|
||
* Small packages
|
||
** Info
|
||
Info is GNU's attempt at better man pages. Most Emacs packages have
|
||
info pages so I'd like nice navigation options.
|
||
#+begin_src emacs-lisp
|
||
(use-package info
|
||
:defer t
|
||
:general
|
||
(nmmap
|
||
:keymaps 'Info-mode-map
|
||
"h" #'evil-backward-char
|
||
"k" #'evil-previous-line
|
||
"l" #'evil-forward-char
|
||
"H" #'Info-history-back
|
||
"L" #'Info-history-forward
|
||
"RET" #'Info-follow-nearest-node))
|
||
#+end_src
|
||
** Display line numbers
|
||
I don't really like line numbers, I find them similar to
|
||
[[*Fringes][fringes]] as useless space, but at least it provides some
|
||
information. Sometimes it can help with doing repeated commands so a
|
||
toggle option is necessary.
|
||
#+begin_src emacs-lisp
|
||
(use-package display-line-numbers
|
||
:defer t
|
||
:commands display-line-numbers-mode
|
||
:general
|
||
(mode-leader
|
||
"l" #'display-line-numbers-mode)
|
||
:init
|
||
(setq-default display-line-numbers-type 'relative))
|
||
#+end_src
|
||
** WAIT esup
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
I used to be able to just use
|
||
[[file:elisp/profiler-dotemacs.el][profile-dotemacs.el]], when my
|
||
Emacs config was smaller, but now it tells me very little information
|
||
about where my setup is inefficient due to the literate config. Just
|
||
found this ~esup~ thing and it works perfectly, exactly how I would
|
||
prefer getting this kind of information. It runs an external Emacs
|
||
instance and collects information from it, so it doesn't require
|
||
restarting Emacs to profile, and I can compile my configuration in my
|
||
current instance to test it immediately.
|
||
|
||
2023-10-16: Unless I'm doing some optimisations or tests, I don't
|
||
really need this in my config at all times. Enable when needed.
|
||
#+begin_src emacs-lisp
|
||
(use-package esup
|
||
:straight t
|
||
:defer t)
|
||
#+end_src
|
||
** Hl-line
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
Highlights the current line.
|
||
#+begin_src emacs-lisp
|
||
(use-package hl-line
|
||
:straight t
|
||
:defer t
|
||
:hook (text-mode-hook . hl-line-mode)
|
||
:hook (prog-mode-hook . hl-line-mode))
|
||
#+end_src
|
||
** Recentf
|
||
Recentf provides a method of keeping track of recently opened files.
|
||
#+begin_src emacs-lisp
|
||
(use-package recentf
|
||
:defer t
|
||
:hook (emacs-startup-hook . recentf-mode))
|
||
#+end_src
|
||
** Avy
|
||
Setup avy with leader. As I use ~avy-goto-char-timer~ a lot, use the
|
||
~C-s~ bind which replaces isearch. Switch isearch to M-s in case I
|
||
need to use it.
|
||
#+begin_src emacs-lisp
|
||
(use-package avy
|
||
:straight t
|
||
:defer t
|
||
:general
|
||
(nmmap
|
||
:keymaps 'override
|
||
"C-s" #'avy-goto-char-timer
|
||
"M-s" #'isearch-forward
|
||
"gp" #'avy-copy-region
|
||
"gP" #'avy-move-region
|
||
"gl" #'avy-goto-line
|
||
"gw" #'avy-goto-word-1))
|
||
#+end_src
|
||
** Ace window
|
||
Though evil provides a great many features in terms of window
|
||
management, ace window can provide some nicer chords for higher
|
||
management of windows (closing, switching, etc).
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package ace-window
|
||
:straight t
|
||
:defer t
|
||
:custom
|
||
(aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
|
||
:general
|
||
(nmmap
|
||
[remap evil-window-next] #'ace-window))
|
||
#+end_src
|
||
** Ace link
|
||
Avy-style link following!
|
||
#+begin_src emacs-lisp
|
||
(use-package ace-link
|
||
:straight t
|
||
:defer t
|
||
:general
|
||
(nmmap
|
||
:keymaps 'override
|
||
"gL" #'ace-link))
|
||
#+end_src
|
||
** Helpful
|
||
Helpful provides a modernised interface for some common help
|
||
commands. I replace ~describe-function~, ~describe-variable~ and
|
||
~describe-key~ by their helpful counterparts.
|
||
#+begin_src emacs-lisp
|
||
(use-package helpful
|
||
:straight t
|
||
:defer t
|
||
:commands (helpful-callable helpful-variable)
|
||
:general
|
||
(general-def
|
||
[remap describe-function] #'helpful-callable
|
||
[remap describe-variable] #'helpful-variable
|
||
[remap describe-key] #'helpful-key)
|
||
:display
|
||
("\\*helpful.*"
|
||
(display-buffer-at-bottom)
|
||
(inhibit-duplicate-buffer . t)
|
||
(window-height . 0.25))
|
||
:config
|
||
(evil-define-key 'normal helpful-mode-map "q" #'quit-window))
|
||
#+end_src
|
||
** Which-key
|
||
Which key uses the minibuffer when performing a keybind to provide
|
||
possible options for the next key.
|
||
#+begin_src emacs-lisp
|
||
(use-package which-key
|
||
:straight t
|
||
:after general
|
||
:config
|
||
(which-key-mode))
|
||
#+end_src
|
||
** (Rip)grep
|
||
Grep is a great piece of software, a necessary tool in any Linux
|
||
user's inventory. By default Emacs has a family of functions to use
|
||
grep, presenting results in a ~compilation~ style. ~grep~ searches
|
||
files, ~rgrep~ searches in a directory using the ~find~ program and
|
||
~zgrep~ searches archives. This is a great solution for a general
|
||
computer environment; essentially all Linux installs will have ~grep~
|
||
and ~find~ installed.
|
||
|
||
Ripgrep is a Rust program that attempts to perform better than grep,
|
||
and it actually does. This is because of a set of optimisations, such
|
||
as checking the =.gitignore= to exclude certain files from being
|
||
searched. The ripgrep package provides utilities to ripgrep projects
|
||
and files for strings. Though [[*Ivy][ivy]] comes with
|
||
~counsel-rg~, it uses Ivy's completion framework rather than the
|
||
~compilation~ style buffers, which sometimes proves very useful.
|
||
|
||
Of course, this requires installing the rg binary which is available
|
||
in most repositories nowadays.
|
||
*** Grep
|
||
I have no use for standard 'grep'; ~counsel-swiper~ does the same
|
||
thing faster and within Emacs lisp. ~rgrep~ is useful though.
|
||
#+begin_src emacs-lisp
|
||
(use-package grep
|
||
:defer t
|
||
:display
|
||
("^\\*grep.*"
|
||
(display-buffer-at-bottom display-buffer-reuse-window)
|
||
(window-height . 0.35)
|
||
(reusable-frames . t))
|
||
:general
|
||
(search-leader
|
||
"d" #'rgrep)
|
||
(nmmap
|
||
:keymaps 'grep-mode-map
|
||
"0" #'evil-beginning-of-line
|
||
"q" #'quit-window
|
||
"i" #'wgrep-change-to-wgrep-mode
|
||
"c" #'recompile)
|
||
(nmmap
|
||
:keymaps 'wgrep-mode-map
|
||
"q" #'evil-record-macro
|
||
"ZZ" #'wgrep-finish-edit
|
||
"ZQ" #'wgrep-abort-changes)
|
||
:config
|
||
;; Without this wgrep doesn't work properly
|
||
(evil-set-initial-state 'grep-mode 'normal))
|
||
#+end_src
|
||
*** rg
|
||
#+begin_src emacs-lisp
|
||
(use-package rg
|
||
:straight t
|
||
:defer t
|
||
:display
|
||
("^\\*\\*ripgrep\\*\\*"
|
||
(display-buffer-at-bottom display-buffer-reuse-window)
|
||
(window-height . 0.35)
|
||
(reusable-frames . t))
|
||
:general
|
||
(search-leader
|
||
"r" #'rg)
|
||
(nmmap
|
||
:keymaps 'rg-mode-map
|
||
"c" #'rg-recompile
|
||
"C" #'rg-rerun-toggle-case
|
||
"]]" #'rg-next-file
|
||
"[[" #'rg-prev-file
|
||
"q" #'quit-window
|
||
"i" #'wgrep-change-to-wgrep-mode)
|
||
:init
|
||
(setq rg-group-result t
|
||
rg-hide-command t
|
||
rg-show-columns nil
|
||
rg-show-header t
|
||
rg-custom-type-aliases nil
|
||
rg-default-alias-fallback "all"
|
||
rg-buffer-name "*ripgrep*")
|
||
:config
|
||
(evil-set-initial-state 'rg-mode 'normal))
|
||
#+end_src
|
||
** Olivetti
|
||
Olivetti provides a focus mode for Emacs, which makes it look a bit
|
||
nicer with fringes. I also define ~+olivetti-mode~ which will
|
||
remember and clear up any window configurations on the frame, then
|
||
when turned off will reinsert them - provides a nice way to quickly
|
||
focus on a buffer.
|
||
#+begin_src emacs-lisp
|
||
(use-package olivetti
|
||
:straight t
|
||
:defer t
|
||
:commands (+olivetti-mode)
|
||
:general
|
||
(mode-leader
|
||
"o" #'+olivetti-mode)
|
||
:init
|
||
(setq-default olivetti-body-width 0.6)
|
||
(setq olivetti-style 'fancy)
|
||
(add-hook 'olivetti-mode-on-hook
|
||
(proc (interactive) (text-scale-increase 1)))
|
||
(add-hook 'olivetti-mode-off-hook
|
||
(proc (interactive) (text-scale-decrease 1)))
|
||
:config
|
||
(defun +olivetti-mode ()
|
||
(interactive)
|
||
(if (not olivetti-mode)
|
||
(progn
|
||
(window-configuration-to-register 1)
|
||
(delete-other-windows)
|
||
(olivetti-mode t))
|
||
(jump-to-register 1)
|
||
(olivetti-mode 0))))
|
||
#+end_src
|
||
** All the Icons
|
||
Nice set of icons with a great user interface to manage them.
|
||
#+begin_src emacs-lisp
|
||
(use-package all-the-icons
|
||
:straight t
|
||
:defer t
|
||
:commands (all-the-icons-insert)
|
||
:general
|
||
(insert-leader
|
||
"e" #'all-the-icons-insert))
|
||
#+end_src
|
||
** Hide mode line
|
||
Custom minor mode to toggle the mode line. Check it out at
|
||
[[file:elisp/hide-mode-line.el][elisp/hide-mode-line.el]].
|
||
#+begin_src emacs-lisp
|
||
(use-package hide-mode-line
|
||
:load-path "elisp/"
|
||
:defer t
|
||
:general
|
||
(mode-leader
|
||
"m" #'hide-mode-line-mode))
|
||
#+end_src
|
||
** Save place
|
||
Saves current place in a buffer permanently, so on revisiting the file
|
||
(even in a different Emacs instance) you go back to the place you were
|
||
at last.
|
||
#+begin_src emacs-lisp
|
||
(use-package saveplace
|
||
:defer t
|
||
:config
|
||
(save-place-mode))
|
||
#+end_src
|
||
** Licensing
|
||
Loads [[file:elisp/license.el][license.el]] for inserting licenses.
|
||
Licenses are important for distribution and attribution to be defined
|
||
clearly.
|
||
#+begin_src emacs-lisp
|
||
(use-package license
|
||
:demand t
|
||
:load-path "elisp/"
|
||
:general
|
||
(insert-leader
|
||
"l" #'+license/insert-copyright-notice
|
||
"L" #'+license/insert-complete-license))
|
||
#+end_src
|
||
** Memory-report
|
||
New feature of Emacs-29, gives a rough report of memory usage with
|
||
some details. Useful to know on a long Emacs instance what could be
|
||
eating up memory.
|
||
#+begin_src emacs-lisp
|
||
(use-package memory-report
|
||
:defer t
|
||
:general
|
||
(leader
|
||
"qm" #'memory-report))
|
||
#+end_src
|
||
** Save minibuffer history
|
||
#+begin_src emacs-lisp
|
||
(use-package savehist
|
||
:defer t
|
||
:config
|
||
(savehist-mode t))
|
||
#+end_src
|
||
** Drag Stuff
|
||
#+begin_src emacs-lisp
|
||
(use-package drag-stuff
|
||
:straight t
|
||
:defer t
|
||
:general
|
||
(nmmap
|
||
"C-M-h" #'drag-stuff-left
|
||
"C-M-j" #'drag-stuff-down
|
||
"C-M-k" #'drag-stuff-up
|
||
"C-M-l" #'drag-stuff-right))
|
||
#+end_src
|
||
** Searching git directories efficiently
|
||
Using [[file:elisp/search.el][search.el]] I can search a set of
|
||
directories particularly efficiently.
|
||
#+begin_src emacs-lisp
|
||
(use-package search
|
||
:defer t
|
||
:load-path "elisp/"
|
||
:general
|
||
(file-leader
|
||
"p" #'+search/find-file
|
||
"S" #'+search/search-all))
|
||
#+end_src
|
||
** Separedit
|
||
Edit anything anywhere all at once!
|
||
#+begin_src emacs-lisp
|
||
(use-package separedit
|
||
:defer t
|
||
:straight t
|
||
:general
|
||
(leader "e" #'separedit)
|
||
:init
|
||
(setq separedit-default-mode 'org-mode
|
||
separedit-remove-trailing-spaces-in-comment t))
|
||
#+end_src
|
||
** lorem ipsum
|
||
Sometimes you need placeholder text for some UI or document. Pretty
|
||
easy to guess what text I'd use.
|
||
#+begin_src emacs-lisp
|
||
(use-package lorem-ipsum
|
||
:straight t
|
||
:general
|
||
(insert-leader
|
||
"p" #'lorem-ipsum-insert-paragraphs))
|
||
#+end_src
|
||
* Applications
|
||
Emacs is basically an operating system whose primary datatype is text.
|
||
Applications are interfaces/environments which serve a variety of
|
||
purposes, but provide a lot of capability.
|
||
** EWW
|
||
Emacs Web Wowser is the inbuilt text based web browser for Emacs. It
|
||
can render images and basic CSS styles but doesn't have a JavaScript
|
||
engine, which makes sense as it's primarily a text interface.
|
||
#+begin_src emacs-lisp
|
||
(use-package eww
|
||
:defer t
|
||
:general
|
||
(app-leader
|
||
"w" #'eww)
|
||
(nmmap
|
||
:keymaps 'eww-mode-map
|
||
"w" #'evil-forward-word-begin
|
||
"Y" #'eww-copy-page-url)
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-eww-setup)))
|
||
#+end_src
|
||
** Calendar
|
||
Calendar is a simple inbuilt application that helps with date
|
||
functionalities. I add functionality to copy dates from the calendar
|
||
to the kill ring and bind it to "Y".
|
||
#+begin_src emacs-lisp
|
||
(use-package calendar
|
||
: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
|
||
:straight t
|
||
: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/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")
|
||
message-auto-save-directory +mail/local-dir
|
||
message-directory +mail/local-dir)
|
||
: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)))
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-notmuch-setup)))
|
||
#+end_src
|
||
*** Smtpmail
|
||
#+begin_src emacs-lisp
|
||
(use-package smtpmail
|
||
:defer t
|
||
: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
|
||
*** Mail signature using fortune
|
||
#+begin_src emacs-lisp
|
||
(use-package fortune
|
||
:init
|
||
(setq fortune-dir "/usr/share/fortune"
|
||
fortune-file "/usr/share/fortune/cookie")
|
||
:config
|
||
(defvar +mail/signature "---------------\nAryadev Chavali\n---------------\n%s")
|
||
(defun +mail/make-signature ()
|
||
(interactive)
|
||
(format +mail/signature
|
||
(with-temp-buffer
|
||
(let ((fortune-buffer-name (current-buffer)))
|
||
(fortune-in-buffer t)
|
||
(if (bolp) (delete-char -1))
|
||
(buffer-string)))))
|
||
(add-hook 'message-setup-hook
|
||
(lambda nil (setq message-signature (+mail/make-signature)))))
|
||
#+end_src
|
||
** Dired
|
||
Dired: Directory editor for Emacs. An incredibly nifty piece of
|
||
software which deeply integrates with Emacs as a whole. Probably the
|
||
best file manager overall and for large scale file system tasks I
|
||
can't think of a better tool than this.
|
||
|
||
Here I setup dired with a few niceties
|
||
+ Hide details by default (no extra stuff from ~ls~)
|
||
+ Omit dot files by default (using ~dired-omit-mode~)
|
||
+ If I have two dired windows open, moving or copying files in one
|
||
dired instance will automatically target the other dired window
|
||
(~dired-dwim~)
|
||
+ If opening an application on a PDF file, suggest ~zathura~
|
||
+ Examine all the subdirectories within the same buffer
|
||
(~+dired/insert-all-subdirectories~)
|
||
#+begin_src emacs-lisp
|
||
(use-package dired
|
||
:demand t
|
||
:commands (dired find-dired)
|
||
:hook
|
||
(dired-mode-hook . auto-revert-mode)
|
||
(dired-mode-hook . dired-hide-details-mode)
|
||
(dired-mode-hook . dired-omit-mode)
|
||
:init
|
||
(setq-default dired-listing-switches "-AFBlu --group-directories-first"
|
||
dired-omit-files "^\\." ; dotfiles
|
||
dired-omit-verbose nil
|
||
dired-dwim-target t
|
||
dired-kill-when-opening-new-dired-buffer t)
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-dired-setup))
|
||
:general
|
||
(nmmap
|
||
:keymaps 'dired-mode-map
|
||
"SPC" nil
|
||
"SPC ," nil
|
||
"(" #'dired-hide-details-mode
|
||
")" #'dired-omit-mode
|
||
"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
|
||
"o" #'dired-omit-mode
|
||
"k" #'dired-prev-subdir
|
||
"j" #'dired-next-subdir
|
||
"K" #'dired-kill-subdir
|
||
"m" #'dired-mark-files-regexp
|
||
"u" #'dired-undo)
|
||
: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
|
||
*** image-dired
|
||
Image dired is a little cherry on top for Dired: the ability to look
|
||
through swathes of images in a centralised fashion while still being
|
||
able to do all the usual dired stuff as well is really cool.
|
||
#+begin_src emacs-lisp
|
||
(use-package dired
|
||
:defer t
|
||
:init
|
||
(setq image-dired-external-viewer "nsxiv")
|
||
:general
|
||
(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))
|
||
#+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
|
||
:straight t
|
||
:after dired
|
||
:general
|
||
(dir-leader
|
||
"g" #'fd-dired))
|
||
#+end_src
|
||
*** wdired
|
||
Similar to [[*(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
|
||
:straight t
|
||
:after dired
|
||
: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
|
||
** Eshell
|
||
*** Why Eshell?
|
||
Eshell is an integrated shell environment for Emacs, written in Emacs
|
||
Lisp. I argue henceforth that it is the best shell/command
|
||
interpreter to use in Emacs.
|
||
|
||
Eshell is unlike the other alternatives in Emacs as it's a /shell/
|
||
first, not a terminal emulator, with the ability to spoof some aspects
|
||
of a terminal emulator (through the shell parser).
|
||
|
||
The killer benefits of eshell (which would appeal particularly to an
|
||
Emacs user) 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 keymaps, display and variables
|
||
Bind some evil-like movements for easy shell usage, a display record
|
||
so when you call eshell it kinda looks like VSCode's terminal popup.
|
||
|
||
NOTE: This mode doesn't allow you to set maps the normal way; you need
|
||
to set keybindings on eshell-mode-hook, otherwise it'll just overwrite
|
||
them.
|
||
#+begin_src emacs-lisp
|
||
(use-package eshell
|
||
:defer t
|
||
:display
|
||
("\\*.*eshell\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.33))
|
||
:init
|
||
(setq eshell-cmpl-ignore-case t
|
||
eshell-cd-on-directory t
|
||
eshell-highlight-prompt nil)
|
||
(add-hook
|
||
'eshell-mode-hook
|
||
(proc
|
||
(interactive)
|
||
(nmap
|
||
:keymaps 'eshell-mode-map
|
||
"0" #'eshell-bol)
|
||
(general-def
|
||
:states '(normal insert)
|
||
:keymaps 'eshell-mode-map
|
||
"C-j" #'eshell-next-matching-input-from-input
|
||
"C-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 prompt
|
||
Here I use my external library
|
||
[[file:elisp/eshell-prompt.el][eshell-prompt]], which provides a more
|
||
dynamic prompt for Eshell. Current features include:
|
||
+ Git (with difference from remote and number of modified files)
|
||
+ Current date and time
|
||
+ A coloured prompt which changes colour based on the exit status of
|
||
the previous command
|
||
|
||
NOTE: I don't defer this package because it doesn't use any eshell
|
||
internals, just standard old Emacs packages.
|
||
#+begin_src emacs-lisp
|
||
(use-package eshell-prompt
|
||
:load-path "elisp/"
|
||
:config
|
||
(defun +eshell/banner-message ()
|
||
(concat (shell-command-to-string "cowfortune") "\n"))
|
||
(setq eshell-prompt-regexp (format "^%s" +eshell-prompt/user-prompt)
|
||
eshell-prompt-function #'+eshell-prompt/make-prompt
|
||
eshell-banner-message '(+eshell/banner-message)))
|
||
#+end_src
|
||
*** Eshell additions
|
||
Using my external library
|
||
[[file:elisp/eshell-additions.el][eshell-additions]], I get a few new
|
||
eshell internal commands and a surface command to open eshell at the
|
||
current working directory.
|
||
|
||
NOTE: I don't defer this package because it autoloads any eshell
|
||
internals that it uses so I'm only loading what I need to. Any
|
||
~eshell/*~ functions need to be known by eshell before launching, so
|
||
if I loaded this ~:after~ eshell then the first instance has no
|
||
knowledge of the new additions.
|
||
#+begin_src emacs-lisp
|
||
(use-package eshell-additions
|
||
:defer t
|
||
:load-path "elisp/"
|
||
:general
|
||
(shell-leader
|
||
"t" #'+eshell/open)
|
||
(leader
|
||
"T" #'+eshell/at-cwd))
|
||
#+end_src
|
||
*** Eshell syntax highlighting
|
||
This package external package adds syntax highlighting to eshell
|
||
(disabling it for remote work). Doesn't require a lot of config
|
||
thankfully.
|
||
#+begin_src emacs-lisp
|
||
(use-package eshell-syntax-highlighting
|
||
:straight t
|
||
:after eshell
|
||
:hook (eshell-mode-hook . eshell-syntax-highlighting-mode))
|
||
#+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
|
||
:straight t
|
||
: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. It's so good that some people use Emacs just to use it.
|
||
It's difficult to describe well without using it, in my opinion, and
|
||
it integrates so well with Emacs that there is very little need to use
|
||
the git CLI ever.
|
||
|
||
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
|
||
:straight t
|
||
: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
|
||
:defer t
|
||
: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
|
||
:defer t
|
||
: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.
|
||
|
||
~calc-mode~ is a calculator system within Emacs that provides a
|
||
diverse array of mathematical operations. It uses reverse polish
|
||
notation, but there is a standard infix algebraic notation mode so
|
||
don't be too shocked. It can do a surprising amount of stuff, such
|
||
as:
|
||
+ finding derivatives/integrals of generic equations
|
||
+ matrix operations
|
||
+ finding solutions for equations, such as for finite degree multi
|
||
variable polynomials
|
||
|
||
It also has this thing called embedded mode. This allows one to
|
||
perform computation within a non ~calc-mode~ buffer. Surround any
|
||
equation with dollar signs (such as 2^20, for example) and call
|
||
~(calc-embedded)~ with your cursor on it to compute it. It'll replace
|
||
the equation with the result it computed.
|
||
|
||
Say I want to find the 4th power of 2 cos I'm writing some bit
|
||
manipulation code and I need to set the 4th bit of some variable to 1.
|
||
Instead of computing it outside of my editor then copying the result
|
||
back in, I can just do it within Emacs. Pretty nifty, right?
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package calc
|
||
:defer t
|
||
: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
|
||
** Zone
|
||
Of course Emacs has a cool screensaver software.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package zone-matrix
|
||
:straight t
|
||
:defer 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
|
||
:defer t
|
||
: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
|
||
:defer t
|
||
: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
|
||
* Text modes
|
||
Standard packages and configurations for text-mode and its derived
|
||
modes.
|
||
** Flyspell
|
||
Flyspell allows me to quickly spell check text documents. I use
|
||
flyspell primarily in org mode, as that is my preferred prose writing
|
||
software, but I also need it in commit messages and so on. So
|
||
flyspell-mode should be hooked to text-mode.
|
||
#+begin_src emacs-lisp
|
||
(use-package flyspell
|
||
:straight t
|
||
:defer t
|
||
:hook (text-mode-hook . flyspell-mode)
|
||
:general
|
||
(nmmap
|
||
:keymaps 'text-mode-map
|
||
(kbd "M-C") #'flyspell-correct-word-before-point
|
||
(kbd "M-c") #'flyspell-auto-correct-word)
|
||
(mode-leader
|
||
"s" #'flyspell-mode))
|
||
#+end_src
|
||
** Undo tree
|
||
Undo tree sits on top of the incredible Emacs undo capabilities.
|
||
Provides a nice visual for edits and a great way to produce branches
|
||
of edits. Also allows saving of undo trees, which makes Emacs a quasi
|
||
version control system in and of itself! The only extra necessary
|
||
would be describing changes...
|
||
#+begin_src emacs-lisp
|
||
(use-package undo-tree
|
||
:demand t
|
||
:straight t
|
||
:general
|
||
(leader
|
||
"u" #'undo-tree-visualize)
|
||
:init
|
||
(setq undo-tree-auto-save-history t
|
||
undo-tree-history-directory-alist backup-directory-alist)
|
||
:config
|
||
(global-undo-tree-mode))
|
||
#+end_src
|
||
** Whitespace
|
||
Deleting whitespace, highlighting when going beyond the 80th character
|
||
limit, all good stuff. I don't want to highlight whitespace for
|
||
general mode categories (Lisp shouldn't really have an 80 character
|
||
limit), so set it for specific modes need the help.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package whitespace
|
||
:defer t
|
||
:general
|
||
(nmmap
|
||
"M--" #'whitespace-cleanup)
|
||
(mode-leader
|
||
"w" #'whitespace-mode)
|
||
:hook
|
||
(before-save-hook . whitespace-cleanup)
|
||
(c-mode-hook . whitespace-mode)
|
||
(c++-mode-hook . whitespace-mode)
|
||
(haskell-mode-hook . whitespace-mode)
|
||
(python-mode-hook . whitespace-mode)
|
||
(org-mode-hook . whitespace-mode)
|
||
(text-mode-hook . whitespace-mode)
|
||
:init
|
||
(setq whitespace-style '(face empty spaces tabs newline trailing lines-char
|
||
tab-mark)
|
||
whitespace-line-column 80))
|
||
#+end_src
|
||
** Set auto-fill-mode for all text-modes
|
||
Auto fill mode automatically newlines text on 80 characters, which
|
||
looks nice and integrates well with Evil's sentence and paragraph text
|
||
objects.
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'text-mode-hook #'auto-fill-mode)
|
||
#+end_src
|
||
** Show-paren-mode
|
||
Show parenthesis for Emacs
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'prog-mode-hook #'show-paren-mode)
|
||
#+end_src
|
||
** Smartparens
|
||
Smartparens is a smarter electric-parens, it's much more aware of
|
||
context and easier to use.
|
||
#+begin_src emacs-lisp
|
||
(use-package smartparens
|
||
:straight t
|
||
:defer t
|
||
:hook
|
||
(prog-mode-hook . smartparens-mode)
|
||
(text-mode-hook . smartparens-mode)
|
||
:config
|
||
(setq sp-highlight-pair-overlay nil
|
||
sp-highlight-wrap-overlay t
|
||
sp-highlight-wrap-tag-overlay t)
|
||
|
||
(let ((unless-list '(sp-point-before-word-p
|
||
sp-point-after-word-p
|
||
sp-point-before-same-p)))
|
||
(sp-pair "'" nil :unless unless-list)
|
||
(sp-pair "\"" nil :unless unless-list))
|
||
(sp-local-pair sp-lisp-modes "(" ")" :unless '(:rem sp-point-before-same-p))
|
||
(require 'smartparens-config))
|
||
#+end_src
|
||
** Thesaurus
|
||
=le-thesaurus= is a great extension for quickly searching up words for
|
||
synonyms or antonyms. I may need it anywhere so I bind it to all
|
||
keymaps. Same with dictionary searching.
|
||
#+begin_src emacs-lisp
|
||
(use-package le-thesaurus
|
||
:straight t
|
||
:defer t
|
||
:display
|
||
("\\*Dictionary\\*"
|
||
(display-buffer-reuse-window display-buffer-same-window)
|
||
(reusable-frames . t))
|
||
:init
|
||
(setq dictionary-server "dict.org")
|
||
:general
|
||
(search-leader
|
||
:infix "w"
|
||
"s" #'le-thesaurus-get-synonyms
|
||
"a" #'le-thesaurus-get-antonyms
|
||
"d" #'dictionary-search))
|
||
#+end_src
|
||
* Programming packages
|
||
Packages that help with programming in general, providing IDE like
|
||
capabilities.
|
||
** Eldoc
|
||
Eldoc presents documentation to the user upon placing ones cursor upon
|
||
any symbol. This is very useful when programming as it:
|
||
- presents the arguments of functions while writing calls for them
|
||
- presents typing and documentation of variables
|
||
|
||
Eldoc box makes the help buffer a hovering box instead of printing it
|
||
in the minibuffer. A lot cleaner.
|
||
|
||
2024-05-31: Eldoc box is a bit useless now that I'm not using frames.
|
||
I prefer the use of the minibuffer for printing documentation now.
|
||
#+begin_src emacs-lisp
|
||
(use-package eldoc
|
||
:defer t
|
||
:hook (prog-mode-hook . eldoc-mode)
|
||
:init
|
||
(global-eldoc-mode 1)
|
||
:general
|
||
(leader
|
||
"h>" #'eldoc-doc-buffer))
|
||
#+end_src
|
||
** Flycheck
|
||
Flycheck is the checking system for Emacs. I don't necessarily like
|
||
having all my code checked all the time, so I haven't added a hook to
|
||
prog-mode as it would be better for me to decide when I want checking
|
||
and when I don't.
|
||
|
||
I've added it to C/C++ mode because I use them regularly and flycheck
|
||
has very little overhead to work there.
|
||
#+begin_src emacs-lisp
|
||
(use-package flycheck
|
||
:straight t
|
||
:defer t
|
||
:commands (flycheck-mode flycheck-list-errors)
|
||
:hook
|
||
(c-mode-hook . flycheck-mode)
|
||
(c++-mode-hook . flycheck-mode)
|
||
:general
|
||
(mode-leader
|
||
"f" #'flycheck-mode)
|
||
(code-leader
|
||
"x" #'flycheck-list-errors
|
||
"j" #'flycheck-next-error
|
||
"k" #'flycheck-previous-error)
|
||
:display
|
||
("\\*Flycheck.*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
:init
|
||
(setq-default flycheck-check-syntax-automatically '(save idle-change new-line mode-enabled))
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-flycheck-setup)))
|
||
#+end_src
|
||
** Eglot
|
||
Eglot is package to communicate with LSP servers for better
|
||
programming capabilities. Interactions with a server provide results
|
||
to the client, done through JSON.
|
||
|
||
NOTE: Emacs 28.1 comes with better JSON parsing, which makes Eglot
|
||
much faster.
|
||
|
||
2023-03-26: I've found Eglot to be useful sometimes, but many of the
|
||
projects I work on don't require a heavy server setup to efficiently
|
||
edit and check for errors; Emacs provides a lot of functionality. So
|
||
by default I've disabled it, using =M-x eglot= to startup the LSP
|
||
server when I need it.
|
||
#+begin_src emacs-lisp
|
||
(use-package eglot
|
||
:defer t
|
||
:general
|
||
(code-leader
|
||
:keymaps 'eglot-mode-map
|
||
"f" #'eglot-format
|
||
"a" #'eglot-code-actions
|
||
"r" #'eglot-rename
|
||
"R" #'eglot-reconnect)
|
||
:init
|
||
(setq eglot-stay-out-of '(flymake)
|
||
eglot-ignored-server-capabilities '(:documentHighlightProvider
|
||
:documentOnTypeFormattingProvider
|
||
:inlayHintProvider))
|
||
:config
|
||
(add-to-list 'eglot-server-programs '((c++-mode c-mode) "clangd")))
|
||
#+end_src
|
||
*** Flycheck-Eglot
|
||
By default Eglot uses the integrated flymake package for error
|
||
reporting. I don't mind flymake, and I think an integrated solution
|
||
which doesn't rely on external packages is always a great idea.
|
||
However, I just personally prefer flycheck and it's become part of my
|
||
mental model when programming. So here's a package which will
|
||
integrate flycheck into Eglot's error reporting.
|
||
|
||
(Funny but also kind of depressing is this issue in Eglot where
|
||
someone requested this integration, which caused a bit of a flame war.
|
||
People are stupid.
|
||
[[https://github.com/joaotavora/eglot/issues/42][no opinion on
|
||
flymake]])
|
||
#+begin_src emacs-lisp
|
||
(use-package flycheck-eglot
|
||
:straight t
|
||
:after (flycheck eglot)
|
||
:hook (eglot-managed-mode-hook . flycheck-eglot-mode))
|
||
#+end_src
|
||
** Indentation
|
||
By default, turn off tabs and set the tab width to two.
|
||
#+begin_src emacs-lisp
|
||
(setq-default indent-tabs-mode nil
|
||
tab-width 2)
|
||
#+end_src
|
||
|
||
However, if necessary later, define a function that may activate tabs locally.
|
||
#+begin_src emacs-lisp
|
||
(defun +oreo/use-tabs ()
|
||
(interactive)
|
||
(setq-local indent-tabs-mode t))
|
||
#+end_src
|
||
** Highlight todo items
|
||
TODO items are highlighted in org-mode, but not necessarily in every
|
||
mode. This minor mode highlights all TODO like items via a list of
|
||
strings to match. It also configures faces to use when highlighting.
|
||
I hook it to prog-mode.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package hl-todo
|
||
:straight t
|
||
:after prog-mode
|
||
:hook (prog-mode-hook . hl-todo-mode)
|
||
:init
|
||
(setq hl-todo-keyword-faces
|
||
'(("TODO" . "#E50000")
|
||
("WIP" . "#ffa500")
|
||
("NOTE" . "#00CC00")
|
||
("FIXME" . "#d02090"))))
|
||
#+end_src
|
||
** Hide-show mode
|
||
Turn on ~hs-minor-mode~ for all prog-mode. This provides folds for
|
||
free.
|
||
#+begin_src emacs-lisp
|
||
(use-package hideshow
|
||
:defer t
|
||
:hook (prog-mode-hook . hs-minor-mode))
|
||
#+end_src
|
||
** Aggressive indenting
|
||
Essentially my dream editing experience: when I type stuff in, try and
|
||
indent it for me on the fly. Just checkout the
|
||
[[https://github.com/Malabarba/aggressive-indent-mode][page]], any
|
||
description I give won't do it justice.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package aggressive-indent
|
||
:straight t
|
||
:demand t
|
||
:config
|
||
(add-to-list 'aggressive-indent-excluded-modes
|
||
'c-mode)
|
||
(add-to-list 'aggressive-indent-excluded-modes
|
||
'c++-mode)
|
||
(add-to-list 'aggressive-indent-excluded-modes
|
||
'cc-mode)
|
||
(global-aggressive-indent-mode))
|
||
#+end_src
|
||
** Compilation
|
||
Compilation mode, a super useful subsystem of Emacs which allows one
|
||
to run arbitrary commands. If those commands produce errors,
|
||
particularly errors that have a filename, column and line,
|
||
compilation-mode can colourise them and automatically help you
|
||
navigate to them. Very nifty.
|
||
|
||
Here I add some bindings and a filter which colourises the output of
|
||
compilation mode for ANSI escape sequences; eyecandy is certainly nice
|
||
but it's just useful when dealing with tools that use those codes so
|
||
you can actually read the text.
|
||
#+begin_src emacs-lisp
|
||
(use-package compile
|
||
:defer t
|
||
:general
|
||
(leader
|
||
"j" #'next-error
|
||
"k" #'previous-error)
|
||
(code-leader
|
||
"c" #'compile
|
||
"C" #'recompile)
|
||
(nmmap
|
||
:keymaps 'compilation-mode-map
|
||
"c" #'recompile)
|
||
(general-def
|
||
:keymaps 'compilation-mode-map
|
||
"g" nil) ;; by default this is recompile
|
||
:display
|
||
("\\*compilation\\*"
|
||
(display-buffer-reuse-window display-buffer-at-bottom)
|
||
(reusable-frames . t)
|
||
(window-height . 0.25))
|
||
:init
|
||
(setq compilation-scroll-output 'first-error)
|
||
:config
|
||
(add-hook 'compilation-filter-hook #'ansi-color-compilation-filter))
|
||
#+end_src
|
||
** xref
|
||
Find definitions, references and general objects using tags without
|
||
external packages. Provided by default in Emacs and just requires a
|
||
way of generating a =TAGS= file for your project. Helps with minimal
|
||
setups for programming without heavier packages like [[*Eglot][Eglot]].
|
||
#+begin_src emacs-lisp
|
||
(use-package xref
|
||
:defer t
|
||
:display
|
||
("\\*xref\\*"
|
||
(display-buffer-at-bottom)
|
||
(inhibit-duplicate-buffer . t)
|
||
(window-height . 0.25))
|
||
:general
|
||
(code-leader
|
||
"t" '(nil :which-key "Tags"))
|
||
(code-leader
|
||
:infix "t"
|
||
"t" #'xref-find-apropos
|
||
"d" #'xref-find-definitions
|
||
"r" #'xref-find-references)
|
||
(nmmap
|
||
:keymaps 'xref--xref-buffer-mode-map
|
||
"RET" #'xref-goto-xref
|
||
"J" #'xref-next-line
|
||
"K" #'xref-prev-line
|
||
"g" #'xref-revert-buffer
|
||
"q" #'quit-window))
|
||
#+end_src
|
||
** Project.el
|
||
An inbuilt solution for creating and managing projects that doesn't
|
||
require a dependency. Where possible we should try to use Emacs
|
||
defaults (admittedly this is a philosophy I've only recently adopted)
|
||
so when setting up a new computer it takes a bit less time.
|
||
|
||
Here I write a TAGS command, mimicking projectile's one, so I can
|
||
quickly generate them in C/C++ projects.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package project
|
||
:defer t
|
||
:general
|
||
(general-def
|
||
:keymaps 'project-prefix-map
|
||
"R" #'+project/generate-tags)
|
||
:config
|
||
(defun +project/generate-tags ()
|
||
(interactive)
|
||
(let ((project (project-current)))
|
||
(if (not project)
|
||
(message "+project/generate-tags: Not in project.")
|
||
(let ((tags-file (concat (project-root project) "TAGS"))
|
||
(files (format "%s" (project-files project))))
|
||
(set-process-sentinel
|
||
(start-process-shell-command
|
||
"PROJECT-GENERATE-TAGS"
|
||
"*gen-tags*"
|
||
(format "ctags -Re -f %s %s"
|
||
tags-file
|
||
(substring files 1 (- (length files) 1))))
|
||
(lambda (p event)
|
||
(when (string= event "finished\n")
|
||
(visit-tags-table (concat (project-root (project-current)) "TAGS"))
|
||
(message "Finished generating tags!")))))))))
|
||
#+end_src
|
||
** devdocs
|
||
#+begin_src emacs-lisp
|
||
(use-package devdocs
|
||
:straight t
|
||
:defer t
|
||
:general
|
||
(file-leader
|
||
"d" #'devdocs-lookup))
|
||
#+end_src
|
||
** rainbow-delimiters
|
||
Makes colours delimiters (parentheses) based on their depth in an
|
||
expression. Rainbow flag in your Lisp source code.
|
||
#+begin_src emacs-lisp
|
||
(use-package rainbow-delimiters
|
||
:defer t
|
||
:straight t
|
||
:general
|
||
(mode-leader "r" #'rainbow-delimiters-mode)
|
||
:hook
|
||
((lisp-mode-hook emacs-lisp-mode-hook racket-mode-hook) . rainbow-delimiters-mode))
|
||
#+end_src
|
||
* Org mode
|
||
Org is, at its most basic, a markup language. =org-mode= is a major
|
||
mode for Emacs to interpret org buffers. org-mode provides a lot of
|
||
capabilities, some are:
|
||
+ A complete table based spreadsheet system, with formulas (including
|
||
[[*Calculator][calc-mode]] integration)
|
||
+ Code blocks with proper syntax highlighting and editing experience
|
||
+ Evaluation
|
||
+ Export of code blocks to a variety of formats
|
||
+ Export of code blocks to a code file (so called "tangling", which
|
||
is what occurs in this document)
|
||
+ Feature complete scheduling system with [[*Calendar][calendar]]
|
||
integration
|
||
+ A clock-in system to time tasks
|
||
+ TODO system
|
||
+ Export to a variety of formats or make your own export engine using
|
||
the org AST.
|
||
+ Inline $\LaTeX$, with the ability to render the fragments on
|
||
demand within the buffer
|
||
+ Links to a variety of formats:
|
||
+ Websites (via http or https)
|
||
+ FTP
|
||
+ SSH
|
||
+ Files (even to a specific line)
|
||
+ Info pages
|
||
|
||
I'd argue this is a bit more than a markup language. Like
|
||
[[*Magit][Magit]], some use Emacs just for this system.
|
||
** Org Essentials
|
||
Org has a ton of settings to tweak, which change your experience quite
|
||
a bit. Here are mine, but this took a lot of just reading other
|
||
people's configurations and testing. I don't do a good job of
|
||
explaining how this works in all honesty, but it works well for me so
|
||
I'm not very bothered.
|
||
|
||
+ By default =~/Text= is my directory for text files. I actually have
|
||
a repository that manages this directory for agenda files and other
|
||
documents
|
||
+ Indentation in file should not be allowed, i.e. text indentation, as
|
||
that forces other editors to read it a certain way as well. It's
|
||
obtrusive hence it's off.
|
||
+ Org startup indented is on by default as most documents do benefit
|
||
from the indentation, but I do turn it off for some files via
|
||
~#+startup:noindent~
|
||
+ When opening an org document there can be a lot of headings, so I
|
||
set folding to just content
|
||
+ Org documents can also have a lot of latex previews, which make
|
||
opening some after a while a massive hassle. If I want to see the
|
||
preview, I'll do it myself, so turn it off.
|
||
+ Org manages windowing itself, to some extent, so I set those options
|
||
to be as unobtrusive as possible
|
||
+ Load languages I use in =src= blocks in org-mode (Emacs-lisp for
|
||
this configuration, C and Python)
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:straight t
|
||
:defer t
|
||
:init
|
||
(setq org-directory "~/Text"
|
||
org-adapt-indentation nil
|
||
org-indent-mode nil
|
||
org-startup-indented t
|
||
org-startup-folded 'content
|
||
org-startup-with-latex-preview nil
|
||
org-imenu-depth 10
|
||
org-src-window-setup 'current-window
|
||
org-indirect-buffer-display 'current-window
|
||
org-link-frame-setup '((vm . vm-visit-folder-other-frame)
|
||
(vm-imap . vm-visit-imap-folder-other-frame)
|
||
(file . find-file))
|
||
org-babel-load-languages '((emacs-lisp . t)
|
||
(lisp . t)
|
||
(shell . t))))
|
||
#+end_src
|
||
** Org Latex
|
||
Org mode has deep integration with latex, can export to PDF and even
|
||
display latex fragments in the document directly. I setup the
|
||
pdf-process, code listing options via minted and the format options
|
||
for latex fragments.
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:defer t
|
||
:init
|
||
(setq org-format-latex-options
|
||
'(:foreground default :background default :scale 2
|
||
:html-foreground "Black" :html-background "Transparent"
|
||
:html-scale 1.0 :matchers ("begin" "$1" "$" "$$" "\\(" "\\["))
|
||
org-latex-src-block-backend 'minted
|
||
org-latex-minted-langs '((emacs-lisp "common-lisp")
|
||
(ledger "text")
|
||
(cc "c++")
|
||
(cperl "perl")
|
||
(shell-script "bash")
|
||
(caml "ocaml"))
|
||
org-latex-packages-alist '(("" "minted"))
|
||
org-latex-pdf-process
|
||
(list (concat "latexmk -f -bibtex -pdf "
|
||
"-shell-escape -%latex -interaction=nonstopmode "
|
||
"-output-directory=%o %f"))
|
||
org-latex-minted-options
|
||
'(("style" "colorful")
|
||
("linenos")
|
||
("frame" "single")
|
||
("mathescape")
|
||
("fontfamily" "courier")
|
||
("samepage" "false")
|
||
("breaklines" "true")
|
||
("breakanywhere" "true"))))
|
||
#+end_src
|
||
** Org Core Variables
|
||
Tons of variables for org-mode, including a ton of latex ones. Can't
|
||
really explain because it sets up quite a lot of local stuff. Also I
|
||
copy pasted the majority of this, tweaking it till it felt good. Doom
|
||
Emacs was very helpful here.
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:defer t
|
||
:init
|
||
(setq org-edit-src-content-indentation 0
|
||
org-goto-interface 'outline
|
||
org-imenu-depth 10
|
||
org-export-backends '(ascii html latex odt icalendar)
|
||
org-eldoc-breadcrumb-separator " → "
|
||
org-enforce-todo-dependencies t
|
||
org-fontify-quote-and-verse-blocks t
|
||
org-fontify-whole-heading-line t
|
||
org-footnote-auto-label t
|
||
org-hide-leading-stars t
|
||
org-hide-emphasis-markers nil
|
||
org-image-actual-width nil
|
||
org-priority-faces '((?A . error) (?B . warning) (?C . success))
|
||
org-link-descriptive nil
|
||
org-tags-column 0
|
||
org-todo-keywords
|
||
'((sequence "TODO" "WIP" "DONE")
|
||
(sequence "PROJ" "WAIT" "COMPLETE"))
|
||
org-use-sub-superscripts '{}))
|
||
#+end_src
|
||
** Org Core Functionality
|
||
Hooks, prettify-symbols and records for auto insertion.
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:defer t
|
||
:hook
|
||
(org-mode-hook . prettify-symbols-mode)
|
||
:display
|
||
("\\*Org Src.*"
|
||
(display-buffer-same-window))
|
||
:auto-insert
|
||
(("\\.org\\'" . "Org skeleton")
|
||
"Enter title: "
|
||
"#+title: " str | (buffer-file-name) "\n"
|
||
"#+author: " (read-string "Enter author: ") | user-full-name "\n"
|
||
"#+description: " (read-string "Enter description: ") | "Description" "\n"
|
||
"#+date: " (format-time-string "%Y-%m-%d" (current-time)) "\n"
|
||
"* " _))
|
||
#+end_src
|
||
** Org Core Bindings
|
||
Some bindings for org mode.
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:defer t
|
||
:general
|
||
(file-leader
|
||
"l" #'org-store-link
|
||
"i" #'org-insert-last-stored-link)
|
||
(code-leader
|
||
:keymaps 'emacs-lisp-mode-map
|
||
"D" #'org-babel-detangle)
|
||
(local-leader
|
||
:keymaps 'org-mode-map
|
||
"l" '(nil :which-key "Links")
|
||
"'" '(nil :which-key "Tables")
|
||
"c" '(nil :which-key "Clocks")
|
||
"r" #'org-refile
|
||
"d" #'org-date-from-calendar
|
||
"t" #'org-todo
|
||
"," #'org-priority
|
||
"T" #'org-babel-tangle
|
||
"i" #'org-insert-structure-template
|
||
"p" #'org-latex-preview
|
||
"s" #'org-property-action
|
||
"e" #'org-export-dispatch
|
||
"o" #'org-edit-special)
|
||
(local-leader
|
||
:keymaps 'org-mode-map
|
||
:infix "l"
|
||
"i" #'org-insert-link
|
||
"l" #'org-open-at-point
|
||
"f" #'org-footnote-action)
|
||
(local-leader
|
||
:keymaps 'org-mode-map
|
||
:infix "'"
|
||
"a" #'org-table-align
|
||
"c" #'org-table-create
|
||
"f" #'org-table-edit-formulas
|
||
"t" #'org-table-toggle-coordinate-overlays
|
||
"s" #'org-table-sum
|
||
"e" #'org-table-calc-current-TBLFM
|
||
"E" #'org-table-eval-formula))
|
||
#+end_src
|
||
** Searching org files
|
||
The default ~imenu~ support for Org-mode is god-awful. ~Imenu~ for
|
||
org-mode should show me a list of headings and provide a
|
||
completing-read interface to search them.
|
||
|
||
[[*Counsel][Counsel]] has me covered for this as I can just provide it
|
||
a regex as an initial prompt to narrow the candidates down to just the
|
||
headings then let the user go from there. I use ~swiper~ when
|
||
considering just the local file (a la ~imenu~) and ~counsel-rg~ to
|
||
search multiple org-files.
|
||
|
||
The cherry on top is ~+org/search-config-headings~ which searches the
|
||
org files in ~user-emacs-directory~ and provides the headings for
|
||
them. This allows me to search my configuration pretty quickly.
|
||
#+begin_src emacs-lisp
|
||
(use-package counsel
|
||
:defer t
|
||
:commands (+org/swiper-goto
|
||
+org/search-headings
|
||
+org/search-config-headings)
|
||
:general
|
||
(file-leader
|
||
"P" #'+org/search-config-headings)
|
||
(search-leader
|
||
:keymaps 'org-mode-map
|
||
"I" #'+org/search-headings)
|
||
(nmmap
|
||
:keymaps 'org-mode-map
|
||
[remap imenu] #'+org/swiper-goto)
|
||
:config
|
||
(defun +org/swiper-goto ()
|
||
(interactive)
|
||
(counsel-grep-or-swiper "^\\* "))
|
||
|
||
(defun +org/search-headings ()
|
||
"Searches directory (of buffer) for org headings via counsel-rg"
|
||
(interactive)
|
||
(counsel-rg "^\\* " (file-name-directory (buffer-file-name))))
|
||
|
||
(defun +org/search-config-headings ()
|
||
"Searches config.org for org headings via +org/swiper-goto"
|
||
(interactive)
|
||
(counsel-rg "^\\* " (file-name-directory user-emacs-directory) "--max-depth=1")))
|
||
#+end_src
|
||
** Org Agenda
|
||
Org agenda provides a nice viewing for schedules. With org mode it's
|
||
a very tidy way to manage your time.
|
||
#+begin_src emacs-lisp
|
||
(use-package org-agenda
|
||
:defer t
|
||
:init
|
||
(defconst +org/agenda-root "~/Text"
|
||
"Root directory for all agenda files")
|
||
(setq org-agenda-files (list (expand-file-name +org/agenda-root))
|
||
org-agenda-window-setup 'current-window
|
||
org-agenda-skip-deadline-prewarning-if-scheduled t
|
||
org-agenda-skip-scheduled-if-done t
|
||
org-agenda-skip-deadline-if-done t
|
||
org-agenda-start-with-entry-text-mode nil)
|
||
:config
|
||
(evil-set-initial-state 'org-agenda-mode 'normal)
|
||
:general
|
||
(file-leader
|
||
"a" `(,(proc (interactive)
|
||
(find-file (completing-read "Enter directory: " org-agenda-files nil t)))
|
||
:which-key "Open agenda directory"))
|
||
(app-leader
|
||
"a" #'org-agenda)
|
||
(nmmap
|
||
:keymaps 'org-agenda-mode-map
|
||
"zd" #'org-agenda-day-view
|
||
"zw" #'org-agenda-week-view
|
||
"zm" #'org-agenda-month-view
|
||
"gd" #'org-agenda-goto-date
|
||
"RET" #'org-agenda-switch-to
|
||
"J" #'org-agenda-later
|
||
"K" #'org-agenda-earlier
|
||
"t" #'org-agenda-todo
|
||
"." #'org-agenda-goto-today
|
||
"," #'org-agenda-goto-date
|
||
"q" #'org-agenda-quit
|
||
"r" #'org-agenda-redo))
|
||
#+end_src
|
||
** Org capture
|
||
Org capture provides a system for quickly "capturing" some information
|
||
into an org file. A classic example is creating a new TODO in a
|
||
todo file, where the bare minimum to record one is:
|
||
+ where was it recorded?
|
||
+ when was it recorded?
|
||
+ what is it?
|
||
Org capture provides a way to do that seamlessly without opening the
|
||
todo file directly.
|
||
#+begin_src emacs-lisp
|
||
(use-package org-capture
|
||
:defer t
|
||
:init
|
||
(setq
|
||
org-default-notes-file (concat org-directory "/todo.org")
|
||
org-capture-templates
|
||
'(("t" "Todo" entry
|
||
(file "")
|
||
"* TODO %?
|
||
%T
|
||
%a")))
|
||
:general
|
||
(leader
|
||
"C" #'org-capture)
|
||
(nmmap
|
||
:keymaps 'org-capture-mode-map
|
||
"ZZ" #'org-capture-finalize
|
||
"ZR" #'org-capture-refile
|
||
"ZQ" #'org-capture-kill))
|
||
#+end_src
|
||
** Org clock-in
|
||
Org provides a nice timekeeping system that allows for managing how
|
||
much time is taken per task. It even has an extensive reporting
|
||
system to see how much time you spend on specific tasks or overall.
|
||
#+begin_src emacs-lisp
|
||
(use-package org-clock
|
||
:after org
|
||
:init
|
||
(defvar +org/clock-out-toggle-report nil
|
||
"Non-nil means update the first clock report in the file every
|
||
time a clock out occurs.")
|
||
:config
|
||
(advice-add #'org-clock-out
|
||
:after
|
||
(proc (interactive)
|
||
(if +org/clock-out-toggle-report
|
||
(org-clock-report t))))
|
||
:general
|
||
(local-leader
|
||
:keymaps 'org-mode-map
|
||
:infix "c"
|
||
"d" #'org-clock-display
|
||
"c" #'org-clock-in
|
||
"o" #'org-clock-out
|
||
"r" #'org-clock-report
|
||
"t" (proc (interactive)
|
||
(setq-local +org/clock-out-toggle-report
|
||
(not +org/clock-out-toggle-report)))))
|
||
#+end_src
|
||
** Org compile to PDF on save
|
||
If ~+org/compile-to-pdf-on-save-p~ is non-nil, then compile to
|
||
\(\LaTeX\) and run an async process to compile it to a PDF. Doesn't
|
||
make Emacs hang (like ~org-latex-export-to-pdf~) and doesn't randomly
|
||
crash (like the async handler for org-export). Works really well with
|
||
~pdf-view-mode~.
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:defer t
|
||
:init
|
||
(defvar +org/compile-to-pdf-on-save-p
|
||
nil
|
||
"Non-nil to activate compile functionality.")
|
||
:general
|
||
(local-leader
|
||
:keymaps 'org-mode-map
|
||
"C" (proc (interactive)
|
||
(if (+org/compile-to-pdf-on-save-f)
|
||
(setq-local +org/compile-to-pdf-on-save-p nil)
|
||
(setq-local +org/compile-to-pdf-on-save-p t))))
|
||
:config
|
||
(+oreo/create-auto-save
|
||
(and (eq major-mode 'org-mode) +org/compile-to-pdf-on-save-p)
|
||
(start-process-shell-command "" "*pdflatex*" (concat "pdflatex -shell-escape "
|
||
(org-latex-export-to-latex)))))
|
||
#+end_src
|
||
** WAIT Org ref
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
For bibliographic stuff in $\LaTeX$ export.
|
||
#+begin_src emacs-lisp
|
||
(use-package org-ref
|
||
:straight t
|
||
:defer t
|
||
:init
|
||
(setq bibtex-files '("~/Text/bibliography.bib")
|
||
bibtex-completion-bibliography '("~/Text/bibliography.bib")
|
||
bibtex-completion-additional-search-fields '(keywords)))
|
||
#+end_src
|
||
*** Org ref ivy integration
|
||
Org ref requires ivy-bibtex to work properly with ivy, so we need to
|
||
set that up as well
|
||
#+begin_src emacs-lisp
|
||
(use-package ivy-bibtex
|
||
:straight t
|
||
:after org-ref
|
||
:config
|
||
(require 'org-ref-ivy))
|
||
#+end_src
|
||
** Org message
|
||
Org message allows for the use of org mode when composing mails,
|
||
generating HTML multipart emails. This integrates the WYSIWYG
|
||
experience with mail in Emacs while also providing powerful text
|
||
features with basically no learning curve (as long as you've already
|
||
learnt the basics of org).
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package org-msg
|
||
:straight t
|
||
:hook
|
||
(message-mode-hook . org-msg-mode)
|
||
(notmuch-message-mode-hook . org-msg-mode)
|
||
:config
|
||
(setq org-msg-options "html-postamble:nil H:5 num:nil ^:{} toc:nil author:nil email:nil \\n:t tex:dvipng"
|
||
org-msg-greeting-name-limit 3)
|
||
|
||
(add-to-list 'org-msg-enforce-css
|
||
'(img latex-fragment-inline
|
||
((transform . ,(format "translateY(-1px) scale(%.3f)"
|
||
(/ 1.0 (if (boundp 'preview-scale)
|
||
preview-scale 1.4))))
|
||
(margin . "0 -0.35em")))))
|
||
#+end_src
|
||
** Org for evil
|
||
Evil org for some nice bindings.
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-org
|
||
:straight t
|
||
:defer t
|
||
:hook (org-mode-hook . evil-org-mode)
|
||
:general
|
||
(nmmap
|
||
:keymaps 'org-mode-map
|
||
"TAB" #'org-cycle))
|
||
#+end_src
|
||
** Org reveal
|
||
Org reveal allows one to export org files as HTML presentations via
|
||
reveal.js. Pretty nifty and it's easy to use.
|
||
#+begin_src emacs-lisp
|
||
(use-package ox-reveal
|
||
:straight t
|
||
:defer t
|
||
:init
|
||
(setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js"
|
||
org-reveal-theme "sky"))
|
||
#+end_src
|
||
** Org superstar
|
||
Org superstar adds unicode symbols for headers, much better than the
|
||
default asterisks.
|
||
#+begin_src emacs-lisp
|
||
(use-package org-superstar
|
||
:straight t
|
||
:defer t
|
||
:hook (org-mode-hook . org-superstar-mode))
|
||
#+end_src
|
||
** Org bookmark
|
||
I maintain a bookmarks file at =~/Text/bookmarks.org=. I would like
|
||
the ability to construct new bookmarks and open bookmarks. They may
|
||
be either articles I want to read, useful information documents or
|
||
just straight up youtube videos. So I wrote a
|
||
[[file:elisp/org-bookmark.el][library]] myself which does the
|
||
appropriate dispatching and work for me. Pretty sweet!
|
||
|
||
Also I define a template for org-capture here for bookmarks and add it
|
||
to the list ~org-capture-templates~.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package org-bookmark
|
||
:defer t
|
||
:load-path "elisp/"
|
||
:general
|
||
(file-leader
|
||
"b" #'+bookmark/open-bookmark)
|
||
:init
|
||
(with-eval-after-load "org-capture"
|
||
(add-to-list
|
||
'org-capture-templates
|
||
'("b" "Bookmark" entry
|
||
(file "bookmarks.org")
|
||
"* %? :bookmark:
|
||
%T
|
||
%^{url|%x}p
|
||
"
|
||
))))
|
||
#+end_src
|
||
* Languages
|
||
For a variety of (programming) languages Emacs comes with default
|
||
modes but this configures them as well as pulls any modes Emacs
|
||
doesn't come with.
|
||
** 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 make-mode
|
||
:defer t
|
||
:auto-insert
|
||
(("[mM]akefile\\'" . "Makefile skeleton")
|
||
""
|
||
"CC=gcc
|
||
OUT=main.out
|
||
LIBS=
|
||
ARGS=
|
||
|
||
GFLAGS=-Wall -Wextra -Werror -Wswitch-enum -std=c11
|
||
DFLAGS=-ggdb -fsanitize=address -fsanitize=undefined
|
||
DEPFLAGS=-MT $@ -MMD -MP -MF
|
||
RFLAGS=-O3
|
||
ifdef RELEASE
|
||
CFLAGS=$(GFLAGS) $(RFLAGS)
|
||
else
|
||
CFLAGS=$(GFLAGS) $(DFLAGS)
|
||
endif
|
||
|
||
SRC=src
|
||
DIST=build
|
||
CODE=$(addprefix $(SRC)/, ) # add source files here
|
||
OBJECTS=$(CODE:$(SRC)/%.c=$(DIST)/%.o)
|
||
DEPDIR:=$(DIST)/dependencies
|
||
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
|
||
** 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
|
||
: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
|
||
: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
|
||
:defer 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 easy
|
||
+ Lots of pretty symbols
|
||
+ Indenting options and a nice (for me) code style for C
|
||
+ 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)
|
||
: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 is a program that formats C/C++ files. It's highly
|
||
configurable and quite fast. I have a root configuration in my
|
||
Dotfiles (check it out
|
||
[[file:~/Dotfiles/ClangFormat/).clang-format][here]].
|
||
|
||
Clang format comes inbuilt with clang/LLVM, so it's quite likely to be
|
||
on your machine.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package clang-format
|
||
:load-path "/usr/share/clang/"
|
||
:defer t
|
||
:after cc-mode
|
||
:commands (+code/clang-format-region-or-buffer
|
||
clang-format-mode)
|
||
:general
|
||
(code-leader
|
||
:keymaps '(c-mode-map c++-mode-map)
|
||
"f" #'clang-format-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 'before-save-hook save-func nil t)
|
||
(remove-hook 'before-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
|
||
** Rust
|
||
#+begin_src emacs-lisp
|
||
(use-package rust-mode
|
||
:straight t
|
||
:defer 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
|
||
** Racket
|
||
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
|
||
:defer t
|
||
:hook (racket-mode-hook . racket-xp-mode)
|
||
:display
|
||
("\\*Racket REPL*"
|
||
(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)
|
||
#+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
|
||
:defer t
|
||
: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
|
||
:straight t
|
||
:defer t
|
||
: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
|
||
: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
|
||
:defer t
|
||
: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
|
||
:straight t
|
||
:defer t
|
||
: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
|
||
:straight t
|
||
: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
|
||
:straight t
|
||
: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, and it's really good in
|
||
comparison to literally everything else. However, I wish it had a
|
||
better REPL...
|
||
*** Sly
|
||
Enter /SLY/. Sly is a fork of /SLIME/ and is *mandatory* for lisp
|
||
development on Emacs.
|
||
|
||
Here I just setup Sly to use ~sbcl~.
|
||
#+begin_src emacs-lisp
|
||
(use-package sly
|
||
:defer t
|
||
:straight t
|
||
:init
|
||
(setq inferior-lisp-program "sbcl"
|
||
sly-lisp-loop-body-forms-indentation 0)
|
||
: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 'normal)
|
||
(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)
|
||
(nmap
|
||
:keymaps 'lisp-mode-map
|
||
"gr" #'sly-eval-buffer
|
||
"gd" #'sly-edit-definition
|
||
"gR" #'sly-who-calls)
|
||
(local-leader
|
||
:keymaps 'lisp-mode-map
|
||
"a" '(sly-apropos :which-key "Apropos")
|
||
"d" '(sly-describe-symbol :which-key "Describe symbol")
|
||
"D" '(sly-documentation-lookup :which-key "Lookup on lispworks")
|
||
"l" '(sly-load-file :which-key "Load file")
|
||
"c" '(sly-compile-defun :which-key "Compile defun")
|
||
"C" '(sly-compile-file :which-key "Compile file")
|
||
"S" '(sly-mrepl-sync :which-key "Sync REPL"))
|
||
(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-mrepl-mode-map
|
||
"M-j" #'sly-mrepl-next-input-or-button
|
||
"M-k" #'sly-mrepl-previous-input-or-button)
|
||
(local-leader
|
||
:keymaps 'sly-mrepl-mode-map
|
||
"s" '(sly-mrepl-shortcut :which-key "Shortcut"))
|
||
(nmap
|
||
:keymaps 'sly-db-mode-map
|
||
"\C-i" 'sly-db-cycle
|
||
"g?" 'describe-mode
|
||
"S" 'sly-db-show-frame-source
|
||
"e" 'sly-db-eval-in-frame
|
||
"d" 'sly-db-pprint-eval-in-frame
|
||
"D" 'sly-db-disassemble
|
||
"i" 'sly-db-inspect-in-frame
|
||
"gj" 'sly-db-down
|
||
"gk" 'sly-db-up
|
||
(kbd "C-j") 'sly-db-down
|
||
(kbd "C-k") 'sly-db-up
|
||
"]]" 'sly-db-details-down
|
||
"[[" 'sly-db-details-up
|
||
(kbd "M-j") 'sly-db-details-down
|
||
(kbd "M-k") 'sly-db-details-up
|
||
"gg" 'sly-db-beginning-of-backtrace
|
||
"G" 'sly-db-end-of-backtrace
|
||
"t" 'sly-db-toggle-details
|
||
"gr" 'sly-db-restart-frame
|
||
"I" 'sly-db-invoke-restart-by-name
|
||
"R" 'sly-db-return-from-frame
|
||
"c" 'sly-db-continue
|
||
"s" 'sly-db-step
|
||
"n" 'sly-db-next
|
||
"o" 'sly-db-out
|
||
"b" 'sly-db-break-on-return
|
||
"a" 'sly-db-abort
|
||
"q" 'sly-db-quit
|
||
"A" 'sly-db-break-with-system-debugger
|
||
"B" 'sly-db-break-with-default-debugger
|
||
"P" 'sly-db-print-condition
|
||
"C" 'sly-db-inspect-condition
|
||
"g:" 'sly-interactive-eval
|
||
"0" 'sly-db-invoke-restart-0
|
||
"1" 'sly-db-invoke-restart-1
|
||
"2" 'sly-db-invoke-restart-2
|
||
"3" 'sly-db-invoke-restart-3
|
||
"4" 'sly-db-invoke-restart-4
|
||
"5" 'sly-db-invoke-restart-5
|
||
"6" 'sly-db-invoke-restart-6
|
||
"7" 'sly-db-invoke-restart-7
|
||
"8" 'sly-db-invoke-restart-8
|
||
"9" 'sly-db-invoke-restart-9)
|
||
(nmap
|
||
:keymaps 'sly-inspector-mode-map
|
||
"q" #'sly-inspector-quit))
|
||
#+end_src
|
||
*** Sly-ASDF
|
||
#+begin_src emacs-lisp
|
||
(use-package sly-asdf
|
||
:straight t
|
||
:after sly)
|
||
#+end_src
|
||
*** Emacs lisp
|
||
#+begin_src emacs-lisp
|
||
(use-package elisp-mode
|
||
:defer t
|
||
:pretty
|
||
(lisp-mode-hook
|
||
("lambda" . "λ")
|
||
("t" . "⊨")
|
||
("nil" . "Ø")
|
||
("and" . "∧")
|
||
("or" . "∨")
|
||
("<=" . "≤")
|
||
(">=" . "≥")
|
||
("defun" . "ƒ")
|
||
("loop" . "Σ")
|
||
("mapcar" . "→")
|
||
("reduce" . "↓")
|
||
("some" . "∃")
|
||
("every" . "∀"))
|
||
(emacs-lisp-mode-hook
|
||
("lambda" . "λ")
|
||
("t" . "⊨")
|
||
("nil" . "Ø")
|
||
("and" . "∧")
|
||
("or" . "∨")
|
||
("defun" . "ƒ")
|
||
("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 [[*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
|
||
:defer t
|
||
: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
|