1815 lines
58 KiB
Org Mode
1815 lines
58 KiB
Org Mode
#+title: Emacs configuration
|
||
#+author: Oreodave
|
||
#+description: My new Emacs configuration
|
||
#+property: header-args:emacs-lisp :tangle config.el :comment link
|
||
#+options: toc:nil
|
||
|
||
#+begin_center
|
||
My configuration for vanilla Emacs
|
||
#+end_center
|
||
#+latex: \clearpage
|
||
#+toc: headlines
|
||
#+latex: \clearpage
|
||
|
||
* Initial
|
||
Let's setup some basics.
|
||
|
||
Firstly, set full name and mail address for use in a variety of
|
||
applications, including encryption.
|
||
#+begin_src emacs-lisp
|
||
(setq user-full-name "Aryadev Chavali"
|
||
user-mail-address "aryadev@aryadevchavali.com")
|
||
#+end_src
|
||
|
||
Let's set all yes or no questions to single letter responses.
|
||
#+begin_src emacs-lisp
|
||
(fset 'yes-or-no-p 'y-or-n-p)
|
||
#+end_src
|
||
|
||
** No literring
|
||
Setup no-littering, which cleans up many of the default directories in
|
||
Emacs.
|
||
#+begin_src emacs-lisp
|
||
(straight-use-package 'no-littering)
|
||
|
||
(setq no-littering-etc-directory (expand-file-name ".config/" user-emacs-directory)
|
||
no-littering-var-directory (expand-file-name ".local/" user-emacs-directory))
|
||
|
||
(require 'no-littering)
|
||
#+end_src
|
||
** File saves and custom file
|
||
Now let's setup file saving and auto-revert-mode. Along with that,
|
||
setup the custom-file to exist in the var-directory
|
||
#+begin_src emacs-lisp
|
||
(use-package emacs
|
||
:straight nil
|
||
:init
|
||
(setq backup-directory-alist `(("." . ,(no-littering-expand-var-file-name "saves/")))
|
||
global-auto-revert-non-file-buffers nil
|
||
auto-revert-verbose nil)
|
||
(setq custom-file (no-littering-expand-etc-file-name "custom.el"))
|
||
:config
|
||
(global-auto-revert-mode 1))
|
||
#+end_src
|
||
** Hide-show mode
|
||
Turn on hs minor mode for all prog-mode.
|
||
#+begin_src emacs-lisp
|
||
(use-package hideshow
|
||
:straight nil
|
||
:hook (prog-mode-hook . hs-minor-mode))
|
||
#+end_src
|
||
** Aesthetics
|
||
Load my custom "Grayscale" theme (look at [[file:Grayscale-theme.el][this file]]).
|
||
#+begin_src emacs-lisp
|
||
(use-package custom
|
||
:straight nil
|
||
:config
|
||
(load-theme 'Grayscale t))
|
||
#+end_src
|
||
|
||
Set font size to 125 if no monitor is plugged in.
|
||
#+begin_src emacs-lisp
|
||
(use-package faces
|
||
:straight nil
|
||
:config
|
||
(set-face-attribute 'default nil :height 115))
|
||
#+end_src
|
||
|
||
Turn off the startup buffer because I don't particularly care for it,
|
||
and write into the scratch buffer some nice information about Emacs.
|
||
#+begin_src emacs-lisp
|
||
(use-package startup
|
||
:straight nil
|
||
:init
|
||
(setq inhibit-startup-screen t
|
||
ring-bell-function 'ignore
|
||
initial-scratch-message (format ";; Emacs v%s\n;; Entered emacs in %s\n"
|
||
emacs-version (emacs-init-time))))
|
||
#+end_src
|
||
* Emacs Mode-line
|
||
Firstly, declare a variable for the separator between each module
|
||
#+begin_src emacs-lisp
|
||
(defconst +modeline/separator " " "Separator between modules.")
|
||
#+end_src
|
||
|
||
Then declare a variable for the number of separators between each
|
||
module in the modeline.
|
||
#+begin_src emacs-lisp
|
||
(defconst +modeline/sep-count 4 "Number of +modline/separator instances separating modules.")
|
||
#+end_src
|
||
|
||
Then, declare a list of reserved characters for which the previously
|
||
declared seperator won't be applied when placed at the end of a module
|
||
string.
|
||
#+begin_src emacs-lisp
|
||
(defconst +modeline/reserved-chars (list "[" "(")
|
||
"Characters that, when at the end of a module string, won't have the separator applied to them.")
|
||
#+end_src
|
||
|
||
Now declare a function that applies the separator with respect to the
|
||
reserved characters to any one string.
|
||
#+begin_src emacs-lisp
|
||
(defun +modeline/handle-string (STR)
|
||
(condition-case nil
|
||
(progn
|
||
(string-blank-p STR)
|
||
(cond ((cl-member (car (last (split-string STR "" t))) +modeline/reserved-chars :test #'string=) STR)
|
||
(t (concat STR (cl-reduce #'concat (cl-loop for i from 1 to +modeline/sep-count collect +modeline/separator))))))
|
||
(error STR)))
|
||
#+end_src
|
||
|
||
Finally, set the mode-line-format.
|
||
#+begin_src emacs-lisp
|
||
(setq-default
|
||
mode-line-format
|
||
(mapcar #'+modeline/handle-string
|
||
(list "%l:%c"
|
||
"%p["
|
||
'(:eval (upcase
|
||
(substring
|
||
(format "%s" (if (bound-and-true-p evil-state) evil-state ""))
|
||
0 1)))
|
||
"]"
|
||
"%+%b("
|
||
'(:eval (format "%s" major-mode))
|
||
")"
|
||
"%I"
|
||
vc-mode
|
||
mode-line-end-spaces)))
|
||
#+end_src
|
||
* Custom Functions
|
||
These are custom functions I have defined
|
||
** New line function
|
||
Vim bindings don't have a nice way of adding new lines before or after
|
||
the current line while staying in normal mode. You can use =o/O= to
|
||
enter insert mode at a new line, but this isn't the same as being able
|
||
to stay in normal mode while opening newlines and only adds extra
|
||
keypresses if your only purpose was to open up some lines.
|
||
|
||
As this is Emacs I can extend it as I wish, so I decided to define a
|
||
new line function that won't remove me from normal state.
|
||
|
||
The logic is pretty simple:
|
||
- Record current location as =old=
|
||
- Use the predefined vim functions for opening new lines above and
|
||
below with insert mode
|
||
- Return to =old=
|
||
- Enter normal state
|
||
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load "evil"
|
||
(defun dx:newline (&optional BACKWARD)
|
||
(interactive)
|
||
(let ((old (point)))
|
||
(cond ((and BACKWARD (= BACKWARD 1)) (evil-open-below 1))
|
||
(t (evil-open-above 1)))
|
||
(goto-char (+ old 1))
|
||
(evil-normal-state))))
|
||
#+end_src
|
||
** Toggle buffer
|
||
*** Preamble
|
||
There are many cases where 'toggling' a buffer is very useful. For
|
||
example, toggling a shell to access it quickly and hide it away with
|
||
little annoyance.
|
||
|
||
This is negligible with a bit of Emacs lisp. However, as stated
|
||
earlier, there are /many/ cases where this is useful. Following the
|
||
DRY principle means a more abstract function would be better to use
|
||
here.
|
||
|
||
One may use higher order functions to create an abstract form that
|
||
handles toggling, and then the caller can wrap this call in a new
|
||
function if they wish to use it in a keybinding. This format or
|
||
construct is kinda common (using a higher order function and wrapping
|
||
it in an interactive function for use in a binding), so I created a
|
||
macro that further wraps this functionality, creating a custom
|
||
function for you.
|
||
|
||
The macro asks for a function name, a buffer name and the function
|
||
necessary to create that function. It then generates a function with
|
||
the given name that holds the necessary logic to 'toggle' buffers.
|
||
*** Code
|
||
#+begin_src emacs-lisp
|
||
(defmacro +dx/create-toggle-function (func-name buf-name buf-create)
|
||
"Generate a function named func-name that toggles
|
||
the buffer with name buf-name and creation function buf-create."
|
||
`(defun ,func-name ()
|
||
(interactive)
|
||
(let* ((buffer (or (get-buffer ,buf-name) (,buf-create)))
|
||
(displayed (get-buffer-window buffer)))
|
||
(cond (displayed
|
||
(select-window displayed)
|
||
(delete-window))
|
||
(t
|
||
(display-buffer buffer)
|
||
(select-window (get-buffer-window buffer)))))))
|
||
#+end_src
|
||
** Power function
|
||
Basic, tail recursive algorithm for calculating powers
|
||
#+begin_src emacs-lisp
|
||
(defun pow (a n &optional initial)
|
||
"Raise a to the nth power. Use init to set the initial value."
|
||
(let ((init (if initial
|
||
initial
|
||
1)))
|
||
(if (= n 0)
|
||
init
|
||
(pow a (- n 1) (* a init)))))
|
||
#+end_src
|
||
** Define procedure
|
||
=lambda= provides a function with possible arguments. A procedure is
|
||
something I define as essentially a function without arguments. This
|
||
macro returns an anonymous function with no arguments with all the
|
||
forms provided. It returns it in 'backquoted' form as that is the most
|
||
common use of this macro.
|
||
#+begin_src emacs-lisp
|
||
(defmacro proc (&rest CDR)
|
||
"For a given list of forms CDR, return a quoted non-argument lambda."
|
||
`(quote (lambda () ,@CDR)))
|
||
#+end_src
|
||
* Core packages
|
||
** General
|
||
Setup general, a good package for defining keys. In this case, I
|
||
generate a new definer for the "LEADER" keys. Leader is bound to SPC
|
||
and it's functionally equivalent the doom/spacemacs leader.
|
||
#+begin_src emacs-lisp
|
||
(use-package general
|
||
:demand t
|
||
:config
|
||
(general-def
|
||
:states 'normal
|
||
"SPC" nil
|
||
"M-V" #'dx:newline
|
||
"M-v" (proc (interactive) (dx:newline 1)))
|
||
|
||
(general-create-definer leader
|
||
:states 'normal
|
||
:keymaps 'override
|
||
:prefix "SPC")
|
||
|
||
(leader
|
||
:infix "b"
|
||
"d" #'kill-this-buffer))
|
||
#+end_src
|
||
*** Some default binds in Emacs
|
||
With a ton of use-package declarations (to defer until the last
|
||
moment), bind to general some basic binds.
|
||
#+begin_src emacs-lisp
|
||
(use-package face-remap
|
||
:straight nil
|
||
:general
|
||
(general-def
|
||
:states 'normal
|
||
"C--" #'text-scale-decrease
|
||
"C-=" #'text-scale-increase))
|
||
|
||
(use-package frame
|
||
:straight nil
|
||
:general
|
||
(general-def
|
||
:states 'normal
|
||
"C-x d" #'delete-frame))
|
||
|
||
(use-package simple
|
||
:straight nil
|
||
:general
|
||
(leader
|
||
"SPC" #'execute-extended-command
|
||
"u" #'universal-argument
|
||
";" #'eval-expression))
|
||
|
||
(use-package files
|
||
:straight nil
|
||
:general
|
||
(leader
|
||
"q" #'save-buffers-kill-terminal
|
||
"cF" (proc (interactive) (find-file "~/Code/")))
|
||
(leader
|
||
:infix "f"
|
||
"f" #'find-file
|
||
"s" #'save-buffer
|
||
"p" (proc (interactive) (find-file (concat user-emacs-directory "config.org")))))
|
||
|
||
(use-package compile
|
||
:straight nil
|
||
:general
|
||
(leader
|
||
"cc" #'compile))
|
||
|
||
(use-package imenu
|
||
:straight nil
|
||
:general
|
||
(leader
|
||
"si" #'imenu))
|
||
|
||
(use-package help
|
||
:straight nil
|
||
:general
|
||
(leader
|
||
"h" #'help-command))
|
||
|
||
(use-package async
|
||
:straight nil
|
||
:general
|
||
(leader
|
||
"!" #'async-shell-command))
|
||
#+end_src
|
||
** Evil
|
||
*** Preamble
|
||
Evil (Emacs VI Layer) is a package that provides the Vi experience to
|
||
Emacs. Packaged with it alone are:
|
||
- Modal system
|
||
- EX
|
||
- Vi mapping functions
|
||
|
||
This provides a lot of stuff for the vim user moving to
|
||
Emacs. However there are many other packages surrounding evil that
|
||
provide even greater functionality from vi to Emacs. Surround,
|
||
commenting, multiple cursors and further support to other packages are
|
||
configured here.
|
||
*** Core
|
||
Setup the evil package, with some basic keybinds.
|
||
#+begin_src emacs-lisp
|
||
(use-package evil
|
||
:defer nil
|
||
:demand t
|
||
:general
|
||
(general-def
|
||
:states 'normal
|
||
[remap evil-window-vsplit] #'make-frame
|
||
"TAB" #'evil-jump-item)
|
||
(general-def
|
||
:states 'visual
|
||
:keymaps 'emacs-lisp-mode-map
|
||
"gr" #'eval-region)
|
||
(leader
|
||
"w" #'evil-window-map
|
||
"wd" #'delete-frame)
|
||
:init
|
||
(setq evil-want-keybinding nil
|
||
evil-split-window-below t
|
||
evil-vsplit-window-right t)
|
||
:config
|
||
(evil-mode))
|
||
#+end_src
|
||
*** Evil surround
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-surround
|
||
:defer nil
|
||
:after evil
|
||
:config
|
||
(global-evil-surround-mode))
|
||
#+end_src
|
||
*** Evil commentary
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-commentary
|
||
:defer nil
|
||
:after evil
|
||
:config
|
||
(evil-commentary-mode))
|
||
#+end_src
|
||
*** Evil mc
|
||
Setup for multicursors 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.
|
||
|
||
Instead, bind some useful functions to my personal =dx:evil-mc-map=
|
||
which is bound to 'gz'. Furthermore, define a function
|
||
=dx:evil-mc-cursor-here= which pauses cursors upon placing a cursor at
|
||
the current position.
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-mc
|
||
:defer nil
|
||
:after evil
|
||
:bind (("M-p" . evil-mc-skip-and-goto-prev-cursor)
|
||
:map dx:evil-mc-map
|
||
("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" . dx:evil-mc-cursor-here)
|
||
("r" . evil-mc-resume-cursors)
|
||
("s" . evil-mc-pause-cursors))
|
||
:init
|
||
(setq evil-mc-key-map nil)
|
||
(define-prefix-command 'dx:evil-mc-map)
|
||
(bind-key "gz" dx:evil-mc-map evil-normal-state-map)
|
||
:config
|
||
(global-evil-mc-mode +1)
|
||
(defun dx:evil-mc-cursor-here ()
|
||
(interactive)
|
||
(evil-mc-make-cursor-here)
|
||
(evil-mc-pause-cursors)))
|
||
#+end_src
|
||
|
||
*** Evil lion
|
||
Evil lion provides alignment operators. Alignment operators allow you
|
||
to, on some given text, align it via a symbol.
|
||
|
||
For example it can transform the following
|
||
#+begin_example
|
||
(James . 19)
|
||
(Arthur . 22)
|
||
#+end_example
|
||
|
||
to
|
||
#+begin_example
|
||
(James . 19)
|
||
(Arthur . 22)
|
||
#+end_example
|
||
|
||
which would be done via =gl<object><symbol>= (in this case =glip.=)
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-lion
|
||
:after evil
|
||
:config
|
||
(evil-lion-mode))
|
||
#+end_src
|
||
*** Evil collection
|
||
Setup evil collection, but don't turn on the mode. Instead, I'll turn
|
||
on setups for specific modes I think benefit from it.
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-collection
|
||
:defer nil
|
||
:after evil
|
||
:config
|
||
(evil-collection-require 'dired)
|
||
(evil-collection-require 'proced))
|
||
#+end_src
|
||
|
||
** Completion
|
||
*** Preamble
|
||
Emacs is a text based interface. As a text based interface it heavily
|
||
leverages searches and user filters to manage input and provide
|
||
functionality. Though the standard model of completion may be
|
||
desirable to some it can be advanced through the use of 'completion
|
||
frameworks'.
|
||
|
||
These frameworks handle the input from the user for common commands
|
||
and provide a differing interface to the one Emacs comes with. Most of
|
||
these completion frameworks provide a text based menu that is actively
|
||
filtered as more input is provided. Along with these frameworks come
|
||
added functionality and applications to integrate into the Emacs
|
||
environment further.
|
||
|
||
One may say that when using a completion framework there is no point
|
||
in using any other framework as they encompasses so much of the
|
||
default functionality. However I'd argue that with a bit of management
|
||
and Emacs lisp it's totally possible to pick and mix your options. For
|
||
small number selections (like finding files) use something like Ido
|
||
and for something larger like searching buffers use ivy.
|
||
*** Ido
|
||
Ido is a very old completion package that still works great to this
|
||
day. Though it is limited in its scope (and may thus be called a
|
||
completion add-on rather than a full on framework), it is still a very
|
||
powerful package. With the use of ido-completing-read+, it may be used
|
||
to as a fully fledged completion framework.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package ido
|
||
:hook (after-init-hook . ido-mode)
|
||
:general
|
||
(general-def
|
||
:keymaps '(ido-buffer-completion-map
|
||
ido-file-completion-map
|
||
ido-file-dir-completion-map
|
||
ido-common-completion-map)
|
||
(kbd "M-j") #'ido-next-match
|
||
(kbd "M-k") #'ido-prev-match)
|
||
(general-def
|
||
[remap find-file] #'ido-find-file
|
||
[remap switch-to-buffer] #'ido-switch-buffer
|
||
[remap dired] #'ido-dired
|
||
[remap make-directory] #'ido-make-directory)
|
||
:init
|
||
(setq ido-separator "\n")
|
||
:config
|
||
(ido-everywhere))
|
||
#+end_src
|
||
**** Ido-completing-read+
|
||
Ido completing-read+ is a package that extends the ido package to work
|
||
with more text based functions.
|
||
#+begin_src emacs-lisp
|
||
(use-package ido-completing-read+
|
||
:after ido
|
||
:config
|
||
(ido-ubiquitous-mode +1))
|
||
#+end_src
|
||
**** Amx
|
||
Amx is a fork of Smex that works to enhance the previous
|
||
interfaces. It also provides support for ido or ivy (though I'm likely
|
||
to use ido here) and allows you to switch between them.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package amx
|
||
:after ido
|
||
:config
|
||
(amx-mode))
|
||
#+end_src
|
||
*** Ivy
|
||
Ivy is a completion framework for Emacs, and my preferred (sometimes
|
||
second favourite) one. It has a great set of features with little to
|
||
no pain with setting up.
|
||
**** Core
|
||
Setup for ivy, in preparation for counsel. Turn on ivy-mode just
|
||
after init.
|
||
|
||
Setup vim-like bindings for the minibuffer ("C-(j|k)" for down|up the
|
||
selection list). Also setup evil-collection for ivy.
|
||
#+begin_src emacs-lisp
|
||
(use-package ivy
|
||
:hook (after-init-hook . ivy-mode)
|
||
:general
|
||
(general-def
|
||
:keymaps 'ivy-minibuffer-map
|
||
"M-j" #'ivy-next-line-or-history
|
||
"M-k" #'ivy-previous-line-or-history
|
||
"C-c C-e" #'ivy-occur)
|
||
(general-def
|
||
:keymaps 'ivy-switch-buffer-map
|
||
"M-j" #'ivy-next-line-or-history
|
||
"M-k" #'ivy-previous-line-or-history)
|
||
:config
|
||
(require 'counsel nil t)
|
||
(setq ivy-height 10
|
||
ivy-wrap t
|
||
ivy-fixed-height-minibuffer t
|
||
ivy-use-virtual-buffers nil
|
||
ivy-virtual-abbreviate 'full
|
||
ivy-on-del-error-function #'ignore
|
||
ivy-use-selectable-prompt t)
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-ivy-setup)))
|
||
#+end_src
|
||
**** Counsel
|
||
Setup for counsel. Load after ivy and helpful.
|
||
|
||
Along with that, set the help function and variable functions to their
|
||
helpful counterparts.
|
||
#+begin_src emacs-lisp
|
||
(use-package counsel
|
||
:after ivy
|
||
:general
|
||
(leader
|
||
"ss" #'counsel-grep-or-swiper
|
||
"sr" #'counsel-rg)
|
||
:init
|
||
(general-def
|
||
[remap describe-function] #'counsel-describe-function
|
||
[remap describe-variable] #'counsel-describe-variable
|
||
[remap describe-bindings] #'counsel-descbinds
|
||
[remap describe-face] #'counsel-faces
|
||
[remap load-theme] #'counsel-load-theme)
|
||
:init
|
||
(setq counsel-describe-function-function #'helpful-callable
|
||
counsel-describe-variable-function #'helpful-variable)
|
||
:config
|
||
(setq ivy-initial-inputs-alist nil))
|
||
#+end_src
|
||
**** Counsel etags
|
||
Counsel etags allows me to search generated tag files for tags. I
|
||
already have a function defined to generate the tags, so it's just
|
||
searching them which I find to be a bit of a hassle, and where this
|
||
package comes in.
|
||
#+begin_src emacs-lisp
|
||
(use-package counsel-etags
|
||
:after counsel
|
||
:general
|
||
(leader "st" #'counsel-etags-find-tag))
|
||
#+end_src
|
||
**** Prompt buffer switch
|
||
Essentially add advice to the window split functions or frame creation
|
||
functions so that they run ivy-switch-buffer once they're finished.
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load "ivy"
|
||
(with-eval-after-load "evil"
|
||
(advice-add #'evil-window-vsplit :after #'ivy-switch-buffer)
|
||
(advice-add #'evil-window-split :after #'ivy-switch-buffer)))
|
||
#+end_src
|
||
** Dired
|
||
Setup for dired. Firstly, as it's an inbuilt package don't let
|
||
straight try and download it. Make dired-hide-details-mode the
|
||
default mode when dired-mode, as it removes the clutter. Create a
|
||
keymap =dx:dired-map= which is bound to the prefix "C-c d", binding
|
||
useful dired functions. Setup evil collection for dired (even though
|
||
dired doesn't really conflict with evil, there are some black corners
|
||
I'd like to adjust)
|
||
#+begin_src emacs-lisp
|
||
(use-package dired
|
||
:straight nil
|
||
:hook (dired-mode-hook . dired-hide-details-mode)
|
||
:general
|
||
(leader
|
||
:infix "d"
|
||
"f" #'find-dired
|
||
"D" #'dired-other-frame
|
||
"d" #'dired-jump)
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-dired-setup)))
|
||
#+end_src
|
||
** IBuffer
|
||
#+begin_src emacs-lisp
|
||
(use-package ibuffer
|
||
:after evil
|
||
:general
|
||
(leader
|
||
"bi" #'ibuffer)
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-ibuffer-setup)))
|
||
#+end_src
|
||
** Magit
|
||
Magit is *the* git porcelain for Emacs, which perfectly encapsulates
|
||
the git cli. In this case, I just need to setup the bindings for it.
|
||
As magit will definitely load after evil (as it must be run by a
|
||
binding, and evil will load after init), I can use evil-collection
|
||
freely.
|
||
#+begin_src emacs-lisp
|
||
(use-package magit
|
||
:general
|
||
(leader "g" #'magit-status))
|
||
|
||
(use-package evil-magit
|
||
:defer nil
|
||
:after magit
|
||
:config
|
||
(evil-magit-init))
|
||
#+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
|
||
:hook
|
||
(prog-mode-hook . company-mode)
|
||
(eshell-mode-hook . company-mode)
|
||
:general
|
||
(general-def
|
||
:states '(normal insert)
|
||
(kbd "C-SPC") #'company-complete)
|
||
(general-def
|
||
:states '(normal insert)
|
||
"M-j" #'company-select-next
|
||
"M-k" #'company-select-previous))
|
||
#+end_src
|
||
** Pretty symbols
|
||
Prettify symbols mode allows for users to declare 'symbols' that
|
||
replace text within certain modes. For example, you may replace the
|
||
'for' word in c-mode in trade of '∀'. Though this may seem like
|
||
useless eye candy, it actually increases my speed of recognition
|
||
(recognising symbols is easier than words for many, including
|
||
me).
|
||
|
||
Now here I provide a macro +pretty/set-alist. This macro works pretty
|
||
simply: given a mode hook, as well as a list of pairs typed (text to
|
||
substitute, symbol to replace with). Then I add a hook to the given
|
||
mode, setting the prettify-symbols-alist to the symbols given.
|
||
|
||
I've declared it pretty high up into my config so that the rest of my
|
||
packages can leverage it.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package prog-mode
|
||
:straight nil
|
||
:init
|
||
(setq prettify-symbols-unprettify-at-point t)
|
||
:config
|
||
(defmacro +pretty/set-alist (mode &rest symbols)
|
||
`(add-hook
|
||
',mode
|
||
(lambda ()
|
||
(setq prettify-symbols-alist ',symbols)
|
||
(prettify-symbols-mode))))
|
||
|
||
(defun +pretty/set-alist-f (mode symbols)
|
||
`(+pretty/set-alist mode ,@symbols)))
|
||
#+end_src
|
||
|
||
Here's a collection of symbols I have currently that may be used
|
||
later.
|
||
#+begin_example
|
||
("null" . "∅")
|
||
("list" . "ℓ")
|
||
("string" . "𝕊")
|
||
("true" . "⊤")
|
||
("false" . "⊥")
|
||
("char" . "ℂ")
|
||
("int" . "ℤ")
|
||
("float" . "ℝ")
|
||
("!" . "¬")
|
||
("&&" . "∧")
|
||
("||" . "∨")
|
||
("for" . "∀")
|
||
("return" . "⟼")
|
||
("print" . "ℙ")
|
||
("lambda" . "λ")
|
||
#+end_example
|
||
** Window management
|
||
Window management is really important. I find the default window
|
||
handling of Emacs incredibly annoying: sometimes consuming my windows,
|
||
sometimes creating new ones. So, as Emacs is the ultimate editor, I
|
||
want to configure and fine tune the window management of Emacs.
|
||
|
||
As I am a man who requires only the highest of optimisations, I always
|
||
am looking for ways to make my system faster. The buffer management
|
||
commands are defined in the window library, so I bind them in general
|
||
here as well via a wrapping use-package declaration.
|
||
#+begin_src emacs-lisp
|
||
(use-package window
|
||
:straight nil
|
||
:general
|
||
(leader
|
||
:infix "b"
|
||
"b" #'switch-to-buffer
|
||
"j" #'next-buffer
|
||
"k" #'previous-buffer)
|
||
:init
|
||
(setq display-buffer-alist
|
||
'(("\\*Org Src.*"
|
||
(display-buffer-same-window))
|
||
("\\*e?shell\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
("\\*[Hh]elp.*"
|
||
(display-buffer-at-bottom)
|
||
(inhibit-duplicate-buffer . t)
|
||
(window-height . 0.25))
|
||
("\\*WoMan.*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
("magit:.*"
|
||
(display-buffer-same-window)
|
||
(inhibit-duplicate-buffer . t))
|
||
("magit-diff:.*"
|
||
(display-buffer-below-selected))
|
||
("magit-log:.*"
|
||
(display-buffer-same-window))
|
||
("\\*compilation\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
("\\*Flycheck.*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
("grep\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
("\\*Python\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
("\\*Org Export.*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
("\\*Async Shell Command\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
("\\*haskell\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
)))
|
||
#+end_src
|
||
* Small packages
|
||
** Projectile
|
||
Setup projectile, along with the tags command. Also bind "C-c C-p" to
|
||
the projectile command map for quick access.
|
||
#+begin_src emacs-lisp
|
||
(use-package projectile
|
||
:after evil
|
||
:hook (prog-mode-hook . projectile-mode)
|
||
:general
|
||
(leader "p" #'projectile-command-map)
|
||
:init
|
||
(setq projectile-tags-command "ctags -Re -f \"%s\" %s \"%s\"")
|
||
:config
|
||
(projectile-global-mode))
|
||
#+end_src
|
||
*** Counsel projectile
|
||
Counsel projectile provides the ivy interface to projectile commands, which is really useful.
|
||
#+begin_src emacs-lisp :tangle no
|
||
(use-package counsel-projectile
|
||
:after (projectile counsel)
|
||
:config
|
||
(counsel-projectile-mode +1))
|
||
#+end_src
|
||
** Hydra
|
||
Use hydras for stuff that I use often, currently buffer manipulation
|
||
#+begin_src emacs-lisp
|
||
(use-package hydra
|
||
:defer nil
|
||
:after evil
|
||
:init
|
||
(defun dx:kill-defun ()
|
||
"Mark defun then kill it."
|
||
(interactive)
|
||
(mark-defun)
|
||
(delete-active-region t))
|
||
|
||
(defun dx:paste-section ()
|
||
"Paste the current kill-region content above section."
|
||
(interactive)
|
||
(open-line 1)
|
||
(yank))
|
||
|
||
:config
|
||
(defhydra hydra-buffer (evil-normal-state-map "SPC b")
|
||
"buffer-hydra"
|
||
("l" next-buffer)
|
||
("h" previous-buffer)
|
||
("c" kill-this-buffer))
|
||
|
||
(defhydra hydra-goto-chg (evil-normal-state-map "g;")
|
||
"goto-chg"
|
||
(";" goto-last-change "goto-last-change")
|
||
("," goto-last-change-reverse "goto-last-change-reverse"))
|
||
|
||
(defhydra hydra-code-manipulator (global-map "C-x c")
|
||
"code-manip"
|
||
("j" evil-forward-section-begin)
|
||
("k" evil-backward-section-begin)
|
||
("m" mark-defun)
|
||
("d" dx:kill-defun)
|
||
("p" dx:paste-section)
|
||
("TAB" evil-toggle-fold)))
|
||
#+end_src
|
||
** Yasnippet
|
||
*** Preamble
|
||
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.
|
||
*** Yasnippet default
|
||
Setup global mode after evil mode has been loaded
|
||
#+begin_src emacs-lisp
|
||
(use-package yasnippet
|
||
:after evil
|
||
:hook
|
||
(prog-mode-hook . yas-minor-mode)
|
||
(text-mode-hook . yas-minor-mode)
|
||
:general
|
||
(leader
|
||
"i" #'yas-insert-snippet)
|
||
:config
|
||
(yas-load-directory (concat user-emacs-directory "snippets")))
|
||
#+end_src
|
||
*** Yasnippet snippets
|
||
Collection of snippets, activate after yasnippet has been loaded.
|
||
#+begin_src emacs-lisp
|
||
(use-package yasnippet-snippets
|
||
:after yasnippet)
|
||
#+end_src
|
||
** Avy
|
||
Setup avy with leader.
|
||
#+begin_src emacs-lisp
|
||
(use-package avy
|
||
:after evil
|
||
:general
|
||
(leader
|
||
:infix "s"
|
||
"l" #'avy-goto-line
|
||
"g" #'avy-goto-char-2))
|
||
#+end_src
|
||
** Ace window
|
||
Though evil provides a great many features in terms of window
|
||
management, much greater than what's easily available in Emacs, ace
|
||
window can provide some nicer chords for higher management of windows
|
||
(closing, switching, etc).
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package ace-window
|
||
:after evil
|
||
:custom
|
||
(aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
|
||
:general
|
||
(general-def
|
||
:states 'normal
|
||
[remap evil-window-next] #'ace-window))
|
||
#+end_src
|
||
** Helpful
|
||
Basic setup, will be fully integrated in counsel.
|
||
#+begin_src emacs-lisp
|
||
(use-package helpful
|
||
:commands (helpful-callable helpful-variable)
|
||
:config
|
||
(evil-define-key 'normal helpful-mode-map "q" #'quit-window))
|
||
#+end_src
|
||
** Which-key
|
||
Pretty simple, just activate after init.
|
||
#+begin_src emacs-lisp
|
||
(use-package which-key
|
||
:defer nil
|
||
:config
|
||
(which-key-mode))
|
||
#+end_src
|
||
** Keychord
|
||
Keychord is only really here for this one chord I wish to define: "jk"
|
||
for exiting insert state. Otherwise, I don't really need it.
|
||
#+begin_src emacs-lisp
|
||
(use-package key-chord
|
||
:after evil
|
||
:config
|
||
(key-chord-define evil-insert-state-map "jk" #'evil-normal-state)
|
||
(key-chord-mode +1))
|
||
#+end_src
|
||
** Ripgrep
|
||
The ripgrep package provides utilities to grep projects and files for
|
||
strings via the rg tool. Though [[*Ivy][ivy]] comes with =counsel-rg= using it
|
||
makes me dependent on the ivy framework, and this configuration is
|
||
intentionally built to be modular and switchable.
|
||
#+begin_src emacs-lisp
|
||
(use-package rg
|
||
:after evil
|
||
:general
|
||
(leader "r" #'rg)
|
||
(:keymaps 'rg-mode-map
|
||
"]]" #'rg-next-file
|
||
"[[" #'rg-prev-file
|
||
"q" #'quit-window)
|
||
: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*"))
|
||
#+end_src
|
||
* Applications
|
||
** Mail
|
||
*** Preamble
|
||
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
|
||
(setq +mail/signature "---------------\nAryadev Chavali")
|
||
(defconst +mail/local-dir (concat user-emacs-directory ".mail/"))
|
||
(defun +mail/sync-mail ()
|
||
"Sync mail via mbsync."
|
||
(interactive)
|
||
(start-process-shell-command "" nil "mbsync -a"))
|
||
|
||
(use-package notmuch
|
||
:commands notmuch
|
||
:general
|
||
(leader "am" #'notmuch)
|
||
:custom
|
||
((notmuch-show-logo nil)
|
||
(mail-signature +mail/signature)
|
||
(mail-default-directory +mail/local-dir)
|
||
(mail-source-directory +mail/local-dir)
|
||
(message-signature +mail/signature)
|
||
(message-auto-save-directory +mail/local-dir)
|
||
(message-directory +mail/local-dir))
|
||
:config
|
||
;; sync mail after refresh
|
||
(advice-add #'notmuch-poll-and-refresh-this-buffer :before
|
||
#'+mail/sync-mail)
|
||
(evil-collection-notmuch-setup))
|
||
#+end_src
|
||
*** Smtpmail
|
||
#+begin_src emacs-lisp
|
||
(use-package smtpmail
|
||
:commands mail-send
|
||
:after notmuch
|
||
:custom
|
||
((smtpmail-smtp-server "mail.aryadevchavali.com")
|
||
(smtpmail-smtp-user "aryadev")
|
||
(smtpmail-smtp-service 587)
|
||
(smtpmail-stream-type 'starttls))
|
||
:init
|
||
(setq send-mail-function #'smtpmail-send-it
|
||
message-send-mail-function #'smtpmail-send-it))
|
||
#+end_src
|
||
*** Org message
|
||
Org message allows for the use of org mode when composing mails,
|
||
generating HTML multipart emails. This integrates the WYSIWYG
|
||
experience into 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
|
||
:after notmuch
|
||
:hook (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-fmt "Dear %s,\n"
|
||
org-msg-greeting-name-limit 3
|
||
org-msg-text-plain-alternative t)
|
||
|
||
(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
|
||
** Xwidget
|
||
*** Preamble
|
||
Xwidget is a package (that must be compiled at source) which allows
|
||
for the insertion of arbitrary xwidgets into Emacs through
|
||
buffers. One of its premier uses is in navigating the web which it
|
||
provides through the function =xwidget-webkit-browse-url=. This
|
||
renders a fully functional web browser within Emacs.
|
||
|
||
Though I am not to keen on using Emacs to browse the web /via/ xwidget
|
||
(EWW does a good job on its own), I am very interested in its
|
||
capability to render full fledged HTML documents, as it may come of
|
||
use when doing web development. I can see the results of work very
|
||
quickly without switching windows or workspaces.
|
||
*** Core
|
||
Define a function =+xwidget/render-file= that reads a file name and
|
||
presents it in an xwidget. If the current file is an HTML file, ask if
|
||
user wants to open current file. Bind it to =au= in the leader.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package xwidget
|
||
:straight nil
|
||
:general
|
||
(leader "aU" #'xwidget-webkit-browse-url)
|
||
(general-def
|
||
:states 'normal
|
||
:keymaps 'xwidget-webkit-mode-map
|
||
"q" #'quit-window
|
||
"h" #'xwidget-webkit-scroll-backward
|
||
"j" #'xwidget-webkit-scroll-up
|
||
"k" #'xwidget-webkit-scroll-down
|
||
"l" #'xwidget-webkit-scroll-forward
|
||
(kbd "C-f") #'xwidget-webkit-scroll-up
|
||
(kbd "C-b") #'xwidget-webkit-scroll-down
|
||
"H" #'xwidget-webkit-back
|
||
"L" #'xwidget-webkit-forward
|
||
"gu" #'xwidget-webkit-browse-url
|
||
"gr" #'xwidget-webkit-reload
|
||
"gg" #'xwidget-webkit-scroll-top
|
||
"G" #'xwidget-webkit-scroll-bottom)
|
||
:config
|
||
(defun +xwidget/render-file (&optional FORCE)
|
||
"Find file (or use current file) and render in xwidget."
|
||
(interactive)
|
||
(cond
|
||
((and (not FORCE) (or (string= (replace-regexp-in-string ".*.html" "html" (buffer-name))
|
||
"html")
|
||
(eq major-mode 'web-mode)
|
||
(eq major-mode 'html-mode))) ; If in html file
|
||
(if (y-or-n-p "Open current file?: ") ; Maybe they want to open a separate file
|
||
(xwidget-webkit-browse-url (format "file://%s" (buffer-file-name)))
|
||
(+xwidget/render-file t))) ; recurse and open file via prompt
|
||
(t
|
||
(xwidget-webkit-browse-url (format "file://%s" (read-file-name "Enter file to open: "))))))
|
||
|
||
(leader "au" #'+xwidget/render-file))
|
||
#+end_src
|
||
|
||
** Eshell
|
||
*** Preamble
|
||
Eshell is the integrated shell environment for Emacs. Though it isn't
|
||
necessarily *the best* shell, it really suits the 'integrated
|
||
computing environment' moniker that Emacs gets.
|
||
|
||
It may be argued that Emacs integrates within itself many of the
|
||
functionalities that one would use within a shell or terminal. Stuff
|
||
like compilation, file management, large scale text manipulation could
|
||
be done through Emacs' own tools (=compile=, =dired= and =occur= come
|
||
to mind). However, I'd argue that eshell's greatest ability comes from
|
||
it's separation (or perhaps better phrased, *integration*) of two
|
||
'parsers': the Lisp parser and the Shell parser. With these parsers
|
||
you can mix and match at will for use in the shell, which grants
|
||
greater power than many shells I know of.
|
||
|
||
*** Configuration
|
||
Setup a function that /toggles/ the eshell window rather than
|
||
just opening it via =+dx/toggle-buffer=.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package eshell
|
||
:commands +shell/toggle-shell
|
||
:general
|
||
(leader
|
||
"tt" #'+shell/toggle-eshell)
|
||
:init
|
||
(setq eshell-cmpl-ignore-case t
|
||
eshell-cd-on-directory t)
|
||
(with-eval-after-load "prog-mode"
|
||
(+pretty/set-alist
|
||
eshell-mode-hook
|
||
("lambda" . "λ")
|
||
("numberp" . "ℤ")
|
||
("t" . "𝕋")
|
||
("nil" . "∅")))
|
||
:config
|
||
(+dx/create-toggle-function +shell/toggle-eshell
|
||
"*eshell*"
|
||
eshell))
|
||
|
||
#+end_src
|
||
** Elfeed
|
||
Elfeed is the perfect RSS feed reader, integrated into Emacs
|
||
perfectly. I've got a set of feeds that I use for a large variety of
|
||
stuff, mostly media and entertainment. I've also bound "<leader> ar"
|
||
to elfeed for loading the system.
|
||
#+begin_src emacs-lisp
|
||
(use-package elfeed
|
||
:general
|
||
(leader "ar" #'elfeed)
|
||
(general-def
|
||
:states 'normal
|
||
:keymaps 'elfeed-search-mode-map
|
||
"gr" #'elfeed-update
|
||
"s" #'elfeed-search-live-filter
|
||
"<return>" #'elfeed-search-show-entry)
|
||
:init
|
||
(setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/"))
|
||
(setq +rss/feed-urls
|
||
'(("Arch Linux"
|
||
"https://www.archlinux.org/feeds/news/"
|
||
Linux)
|
||
("LEMMiNO"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCRcgy6GzDeccI7dkbbBna3Q"
|
||
YouTube Stories)
|
||
("Dark Sominium"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UC_e39rWdkQqo5-LbiLiU10g"
|
||
YouTube Stories)
|
||
("Dark Sominium Music"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCkLiZ_zLynyNd5fd62hg1Kw"
|
||
YouTube Music)
|
||
("Nexpo"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw"
|
||
YouTube)
|
||
("Techquickie"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UC0vBXGSyV14uvJ4hECDOl0Q"
|
||
YouTube)
|
||
("Captain Sinbad"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UC8XKyvQ5Ne_bvYbgv8LaIeg"
|
||
YouTube)
|
||
("3B1B"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw"
|
||
YouTube)
|
||
("Fredrik Knusden"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCbWcXB0PoqOsAvAdfzWMf0w"
|
||
YouTube Stories)
|
||
("Barely Sociable"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UC9PIn6-XuRKZ5HmYeu46AIw"
|
||
YouTube Stories)
|
||
("Atrocity Guide"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCn8OYopT9e8tng-CGEWzfmw"
|
||
YouTube Stories)
|
||
("Philip Defranco"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UClFSU9_bUb4Rc6OYfTt5SPw"
|
||
YouTube News)
|
||
("Hacker News"
|
||
"http://morss.aryadevchavali.com/news.ycombinator.com/rss"
|
||
Social)
|
||
("Hacker Factor"
|
||
"https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2"
|
||
Social)
|
||
("BBC Top News"
|
||
"http://morss.aryadevchavali.com/feeds.bbci.co.uk/news/rss.xml"
|
||
News)
|
||
("BBC Tech News"
|
||
"http://morss.aryadevchavali.com/feeds.bbci.co.uk/news/technology/rss.xml"
|
||
News)))
|
||
(setq elfeed-db-directory (concat user-emacs-directory "elfeed"))
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-elfeed-setup))
|
||
(setq elfeed-feeds (cl-map 'list #'(lambda (item) (append (list (nth 1 item)) (cdr (cdr item)))) +rss/feed-urls)))
|
||
#+end_src
|
||
* Major modes, programming and text
|
||
Setups for common major modes and languages.
|
||
** Text modes
|
||
Standard packages and configurations for the text-mode. These
|
||
configurations are usually further placed on
|
||
*** 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
|
||
:hook (text-mode-hook . flyspell-mode)
|
||
:general
|
||
(general-def
|
||
:states 'normal
|
||
:keymaps 'text-mode-map
|
||
(kbd "M-a") #'flyspell-correct-word-before-point
|
||
(kbd "M-A") #'flyspell-auto-correct-word))
|
||
#+end_src
|
||
*** Display line numbers mode
|
||
Display line numbers: what else to say? I don't like it on in every
|
||
buffer, I like it more as a toggle option. Also, relative line numbers
|
||
suck so set them to absolute. For big files I'll know not to turn it
|
||
on anyway.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package display-line-numbers
|
||
:straight nil
|
||
:general
|
||
(leader
|
||
"tl" #'display-line-numbers-mode)
|
||
:init
|
||
(setq display-line-numbers-type 'absolute))
|
||
#+end_src
|
||
*** White-space management
|
||
Deleting whitespace, highlighting when going beyond the 80th character
|
||
limit, all good stuff.
|
||
|
||
I don't want to highlight whitespace for general mode categories
|
||
(Emacs lisp shouldn't really have an 80 character limit; it's a bit of
|
||
a wild gun), so set it for specific modes I find need the help.
|
||
#+begin_src emacs-lisp
|
||
(use-package whitespace
|
||
:straight nil
|
||
:general
|
||
(general-def
|
||
:states 'normal
|
||
"M--" #'whitespace-cleanup)
|
||
:hook
|
||
(c-mode-hook . whitespace-mode)
|
||
(c++-mode-hook . whitespace-mode)
|
||
(haskell-mode-hook . whitespace-mode)
|
||
(python-mode-hook . whitespace-mode)
|
||
:init
|
||
(setq whitespace-style '(face lines-tail)
|
||
whitespace-line-column 80)
|
||
|
||
(defun +config/show-trailing-whitespace ()
|
||
"Show the trailing whitespace. For use in hooks."
|
||
(setq show-trailing-whitespace t))
|
||
|
||
(add-hook 'c-mode-hook #'+config/show-trailing-whitespace)
|
||
(add-hook 'c++-mode-hook #'+config/show-trailing-whitespace)
|
||
(add-hook 'haskell-mode-hook #'+config/show-trailing-whitespace)
|
||
(add-hook 'python-mode-hook #'+config/show-trailing-whitespace))
|
||
#+end_src
|
||
*** Set auto-fill-mode for all text-modes
|
||
Auto fill mode is nice for most text modes, 80 char limit is great.
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'text-mode-hook #'auto-fill-mode)
|
||
#+end_src
|
||
*** Delete a sentence in auto fill
|
||
In long lines via truncate lines, deleting till the end of the
|
||
sentence was easy via vim motions. However, the same action is
|
||
difficult with auto-fill-mode where sentences are separated through
|
||
(potentially several) newlines which makes vim motions
|
||
difficult. Thus, I propose some form of functionality which allows you
|
||
to:
|
||
|
||
- Find the next closest period denoting the end of the sentence
|
||
- Delete the region between the point of invocation and the found period
|
||
|
||
This essentially does the same task as vim motion based deletion, but
|
||
can handle the newlines. To not trample on the toes of any package,
|
||
I'll set it to "M-d" (kill-word), the most inoffensive binding
|
||
possible which is still mnemonic.
|
||
|
||
First, the function. I'll use search-forward (from zap* lib) to find
|
||
the period. Then auto-fill to make it look nice.
|
||
#+begin_src emacs-lisp
|
||
(defun +text/delete-till-sentence ()
|
||
"Delete all text from current point to the next closest period."
|
||
(interactive)
|
||
(set-mark-command nil)
|
||
(search-forward ". ")
|
||
(kill-region (region-beginning) (region-end))
|
||
(fill-paragraph))
|
||
#+end_src
|
||
|
||
Now, the binding
|
||
#+begin_src emacs-lisp
|
||
(general-def
|
||
:states '(normal insert)
|
||
(kbd "M-d") #'+text/delete-till-sentence)
|
||
#+end_src
|
||
** PDF
|
||
*** Preamble
|
||
PDFs are a great format for (somewhat) immutable text and reports with
|
||
great formatting options. Though Emacs isn't really the premier
|
||
solution for viewing PDFs (I highly recommend [[https://pwmt.org/projects/zathura/][Zathura]]), similar to
|
||
most things with Emacs, having a PDF viewer builtin can be a very
|
||
useful asset.
|
||
|
||
For example if I were editing an org document which I was eventually
|
||
compiling into a PDF, my workflow would be much smoother with a PDF
|
||
viewer within Emacs that I can open on another pane.
|
||
|
||
Furthermore many governmental studies and essays use the PDF
|
||
format. If I were to be analysing them in a study or project (for
|
||
example, programming a tool using data from them), which I will most
|
||
definitely be using Emacs for, having a PDF pane open for occasional
|
||
viewing can be very useful.
|
||
|
||
*** PDF Tools
|
||
=pdf-tools= provides the necessary functionality for viewing
|
||
PDFs. There is no PDF viewing without this package. =evil-collection=
|
||
provides a setup for this mode, so use that.
|
||
#+begin_src emacs-lisp
|
||
(use-package pdf-tools
|
||
:after evil-collection
|
||
:mode ("\\.[pP][dD][fF]" . pdf-view-mode)
|
||
:config
|
||
(pdf-tools-install)
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-pdf-setup)))
|
||
#+end_src
|
||
*** PDF grep
|
||
PDF grep is a Linux tool that allows for searches against PDFs similar
|
||
to standard grep (but for PDFs!). It's a bit badly configured (why not
|
||
use the current buffer?) but it works out.
|
||
#+begin_src emacs-lisp
|
||
(use-package pdfgrep
|
||
:hook (pdf-view-mode . pdfgrep-mode)
|
||
:after pdf-tools
|
||
:general
|
||
(general-def
|
||
:states 'normal
|
||
:keymaps 'pdf-view-mode-hook
|
||
"M-g" #'pdfgrep))
|
||
#+end_src
|
||
** Org
|
||
*** Core
|
||
Setup for org mode, currently basically nothing. Has evil-org for
|
||
evil bindings.
|
||
|
||
Also setup a lot of variables, particularly for latex exports.
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:hook
|
||
(org-mode-hook . yas-minor-mode)
|
||
(org-mode-hook . org-shifttab)
|
||
(org-mode-hook . prettify-symbols-mode)
|
||
:general
|
||
(with-eval-after-load "counsel"
|
||
(general-def
|
||
:keymap 'org-mode-map
|
||
[remap org-goto] #'counsel-org-goto))
|
||
:init
|
||
(with-eval-after-load "prog-mode"
|
||
(+pretty/set-alist
|
||
org-mode-hook
|
||
("#+begin_src" . "≫")
|
||
("#+end_src" . "≪")))
|
||
:custom
|
||
((org-edit-src-content-indentation 0)
|
||
(org-src-window-setup 'current-window)
|
||
(org-indirect-buffer-display 'current-window)
|
||
(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 'plain)
|
||
(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-startup-indented t)
|
||
(org-tags-column 0)
|
||
(org-todo-keywords
|
||
'((sequence "TODO" "WAIT" "DONE")
|
||
(sequence "PROJ" "WAIT" "COMPLETE")))
|
||
(org-use-sub-superscripts '{})
|
||
(org-latex-listings 'minted)
|
||
(org-babel-load-languages '((emacs-lisp . t)
|
||
(C . t)))
|
||
(org-latex-packages-alist '(("" "minted")))
|
||
(org-latex-pdf-process '("%latex -interaction nonstopmode -shell-escape -output-directory %o %f"
|
||
"%latex -interaction nonstopmode -shell-escape -output-directory %o %f"
|
||
"%latex -interaction nonstopmode -shell-escape -output-directory %o %f"))
|
||
(org-latex-minted-options '(("style" "xcode")
|
||
("linenos")
|
||
("frame" "single")
|
||
("mathescape")
|
||
("fontfamily" "courier")
|
||
("samepage" "false")
|
||
("breaklines" "true")
|
||
("breakanywhere" "true")
|
||
))))
|
||
|
||
(use-package evil-org
|
||
:hook (org-mode-hook . evil-org-mode))
|
||
#+end_src
|
||
*** Org fragtog
|
||
Toggle latex fragments in org mode so you get fancy maths symbols. I
|
||
use latex a bit in org mode as it is the premier way of getting
|
||
mathematical symbols and text rendered and compiled, but org mode >
|
||
latex.
|
||
|
||
As Org mode has the ability to accept arbitrary inputs of Latex
|
||
(through escaped (square) brackets), allowing me to observe how they
|
||
look is nice to have.
|
||
#+begin_src emacs-lisp
|
||
(use-package org-fragtog
|
||
:hook (org-mode-hook . org-fragtog-mode))
|
||
#+end_src
|
||
*** Org pretty tables
|
||
Make the default ASCII tables of org mode pretty with
|
||
#+begin_src emacs-lisp
|
||
(use-package org-pretty-table-mode
|
||
:straight (org-pretty-table-mode :type git :host github :repo "Fuco1/org-pretty-table")
|
||
:hook org-mode-hook)
|
||
#+end_src
|
||
*** Org pretty tags
|
||
#+begin_src emacs-lisp
|
||
(use-package org-pretty-tags
|
||
:hook (org-mode-hook . org-pretty-tags-mode))
|
||
#+end_src
|
||
*** Org superstar
|
||
Org superstar adds cute little unicode symbols for headers, much
|
||
better than the default asterisks.
|
||
#+begin_src emacs-lisp
|
||
(use-package org-superstar
|
||
:hook (org-mode-hook . org-superstar-mode))
|
||
#+end_src
|
||
** Colourising compilation
|
||
Colourising the compilation buffer so ansi color codes get computed.
|
||
#+begin_src emacs-lisp
|
||
(use-package compilation
|
||
:straight nil
|
||
:config
|
||
(defun +compile/colourise ()
|
||
"Colourise the emacs compilation buffer."
|
||
(let ((inhibit-read-only t))
|
||
(ansi-color-apply-on-region (point-min) (point-max))))
|
||
(add-hook 'compilation-filter-hook #'+compile/colourise))
|
||
#+end_src
|
||
** Core text manipulation
|
||
*** Smartparens
|
||
Smartparens is a smarter electric-parens, it's much more aware of
|
||
stuff and easier to use.
|
||
#+begin_src emacs-lisp
|
||
(use-package smartparens
|
||
:hook
|
||
(prog-mode-hook . smartparens-mode)
|
||
(text-mode-hook . smartparens-mode)
|
||
:after evil
|
||
: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
|
||
*** Show-paren-mode
|
||
Show parenthesis for Emacs
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'prog-mode-hook #'show-paren-mode)
|
||
#+end_src
|
||
** Coding
|
||
*** 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
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package eldoc
|
||
:straight nil
|
||
:hook (prog-mode-hook . eldoc-mode)
|
||
:init
|
||
(global-eldoc-mode 1))
|
||
|
||
(use-package eldoc-box
|
||
:hook (eldoc-mode-hook . eldoc-box-hover-mode)
|
||
:init
|
||
(setq eldoc-box-position-function #'eldoc-box--default-upper-corner-position-function
|
||
eldoc-box-clear-with-C-g t))
|
||
#+end_src
|
||
*** Eglot
|
||
Eglot is a library of packages to communicate with LSP servers for
|
||
better programming capabilities. Interactions with a server provide
|
||
results to the client, done through JSON.
|
||
#+begin_src emacs-lisp
|
||
(use-package eglot
|
||
:hook
|
||
(c++-mode-hook . eglot-ensure)
|
||
(c-mode-hook . eglot-ensure)
|
||
:bind (:map eglot-mode-map
|
||
("<f2>" . eglot-rename))
|
||
:general
|
||
(leader
|
||
:keymaps '(eglot-mode-map)
|
||
:infix "c"
|
||
"f" #'eglot-format
|
||
"a" #'eglot-code-actions
|
||
"r" #'eglot-rename)
|
||
:config
|
||
(add-to-list 'eglot-server-programs
|
||
'((c++-mode c-mode) "clangd")))
|
||
#+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.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package flycheck
|
||
:commands (flycheck-mode flycheck-list-errors)
|
||
:general
|
||
(leader
|
||
"tf" #'flycheck-mode
|
||
"cx" #'flycheck-list-errors)
|
||
:init
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-flycheck-setup)))
|
||
#+end_src
|
||
*** Activate tabs
|
||
Set tabs to nil by default, with normal tab size set to 2.
|
||
#+begin_src emacs-lisp
|
||
(setq-default indent-tabs-mode nil
|
||
tab-width 2)
|
||
#+end_src
|
||
|
||
Add a function to activate tabs mode for any modes you want tabs in.
|
||
#+begin_src emacs-lisp
|
||
(defun dx:activate-tabs ()
|
||
(interactive)
|
||
(setq indent-tabs-mode t))
|
||
#+end_src
|
||
** C/C++
|
||
Setup for C and C++ modes via the cc-mode package.
|
||
*** Preamble
|
||
C and C++ are great languages for general purpose programming. Though
|
||
lisp is more aesthetically and mentally pleasing, they get the job
|
||
done. Furthermore, they provide speed and finer control in trade of
|
||
aesthetics and security-based abstractions.
|
||
|
||
When writing C/C++ code, I use folds and section manipulation quite a
|
||
bit so observing folds is quite important for me when considering a
|
||
codebase. Thus, I observed the two main styles of brace placement and
|
||
how they do folds.
|
||
|
||
#+begin_src c :tangle no
|
||
if (cond) {...}
|
||
#+end_src
|
||
vs
|
||
#+begin_src c :tangle no
|
||
if (cond)
|
||
{....}
|
||
#+end_src
|
||
|
||
I don't print my code, nor am I absolutely pressed for screen real
|
||
estate in terms of height (such that newlines matter). Width matters
|
||
to me as I do use Emacs multiplexing capabilities often. Thus, with
|
||
these in mind the open brace style is a better option than the
|
||
opposing style.
|
||
|
||
Also, with large code bases consistency is important. I personally use
|
||
tabs as they are more accessible: anyone can set their tab width such
|
||
that it best suits them. Furthermore, tabs produce smaller source
|
||
files. However, this isn't set in stone and I will return to no tabs
|
||
when needed in projects.
|
||
|
||
*** Configuration
|
||
#+begin_src emacs-lisp
|
||
(use-package cc-mode
|
||
:hook
|
||
(c-mode-hook . dx:activate-tabs)
|
||
(c++-mode-hook . dx:activate-tabs)
|
||
:init
|
||
(setq-default c-basic-offset 2)
|
||
(setq c-default-style '((java-mode . "java")
|
||
(awk-mode . "awk")
|
||
(other . "user")))
|
||
|
||
(with-eval-after-load "prog-mode"
|
||
(+pretty/set-alist
|
||
c-mode-hook
|
||
("->" . "→")
|
||
("NULL" . "∅")
|
||
("true" . "𝕋")
|
||
("false" . "𝔽")
|
||
("char" . "ℂ")
|
||
("int" . "ℤ")
|
||
("float" . "ℝ")
|
||
("bool" . "𝔹")
|
||
("!" . "¬")
|
||
("&&" . "∧")
|
||
("||" . "∨")
|
||
("for" . "∀")
|
||
("return" . "⟼"))
|
||
|
||
(+pretty/set-alist
|
||
c++-mode-hook
|
||
("nullptr" . "∅")
|
||
("vector" . "𝕃")
|
||
("std::string" . "𝕊")
|
||
("string" . "𝕊")
|
||
("->" . "→")
|
||
("NULL" . "∅")
|
||
("true" . "𝕋")
|
||
("false" . "𝔽")
|
||
("char" . "ℂ")
|
||
("int" . "ℤ")
|
||
("float" . "ℝ")
|
||
("bool" . "𝔹")
|
||
("!" . "¬")
|
||
("&&" . "∧")
|
||
("||" . "∨")
|
||
("for" . "∀")
|
||
("return" . "⟼")))
|
||
: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 . +)
|
||
(knr-argdecl-intro . 0)
|
||
(substatement-open . 0)
|
||
(substatement-label . 0)
|
||
(access-label . 0)
|
||
(label . 0)
|
||
(statement-cont . +)))))
|
||
#+end_src
|
||
*** Clang format
|
||
Clang format for when:
|
||
- eglot isn't working/I'm not running it
|
||
- eglot format is bad
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package clang-format
|
||
:after cc-mode
|
||
:config
|
||
(bind-key "C-c '" #'clang-format-region c-mode-map)
|
||
(bind-key "C-c '" #'clang-format-region c++-mode-map))
|
||
#+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.
|
||
|
||
Here I configure the REPL for Haskell via the
|
||
=interactive-haskell-mode= as well.
|
||
#+begin_src emacs-lisp
|
||
(use-package haskell-mode
|
||
:hook
|
||
(haskell-mode-hook . haskell-indentation-mode)
|
||
(haskell-mode-hook . interactive-haskell-mode)
|
||
:general
|
||
(leader
|
||
"th" #'+shell/toggle-haskell-repl)
|
||
:config
|
||
(+dx/create-toggle-function
|
||
+shell/toggle-haskell-repl
|
||
"*haskell*"
|
||
haskell-process-start))
|
||
#+end_src
|
||
** Python
|
||
Basic, haven't used python in this configuration yet.
|
||
#+begin_src emacs-lisp
|
||
(use-package python
|
||
:straight nil
|
||
:init
|
||
(setq python-indent-offset 4))
|
||
#+end_src
|
||
*** Python shell
|
||
Setup for python shell, including a toggle option
|
||
#+begin_src emacs-lisp
|
||
(use-package python
|
||
:straight nil
|
||
:after eshell
|
||
:commands +python/toggle-repl
|
||
:general
|
||
(leader
|
||
"tp" #'+python/toggle-repl)
|
||
:config
|
||
(+dx/create-toggle-function +python/toggle-repl
|
||
"*Python*"
|
||
run-python))
|
||
#+end_src
|
||
** HTML/CSS/JS
|
||
Firstly, web mode for consistent colouring of syntax.
|
||
#+begin_src emacs-lisp
|
||
(use-package web-mode
|
||
:mode ("\\.html" . web-mode)
|
||
:mode ("\\.js" . web-mode)
|
||
:mode ("\\.css" . web-mode)
|
||
:custom
|
||
((web-mode-markup-indent-offset 2)
|
||
(web-mode-css-indent-offset 2)))
|
||
#+end_src
|
||
|
||
Then emmet for super speed
|
||
#+begin_src emacs-lisp
|
||
(use-package emmet-mode
|
||
:hook (web-mode-hook . emmet-mode)
|
||
:general
|
||
(general-def
|
||
:states 'insert
|
||
:keymaps 'emmet-mode-keymap
|
||
"TAB" #'emmet-expand-line
|
||
"M-j" #'emmet-next-edit-point
|
||
"M-k" #'emmet-prev-edit-point))
|
||
#+end_src
|
||
** Emacs lisp
|
||
Add a new lisp indent function which indents newline lists more
|
||
appropriately.
|
||
#+begin_src emacs-lisp
|
||
(use-package lisp-mode
|
||
:straight nil
|
||
:init
|
||
(with-eval-after-load "prog-mode"
|
||
(+pretty/set-alist
|
||
emacs-lisp-mode-hook
|
||
("lambda" . "λ")
|
||
("numberp" . "ℤ")
|
||
("t" . "𝕋")
|
||
("nil" . "∅")
|
||
("and" . "∧")
|
||
("or" . "∨")
|
||
("for" . "∀")
|
||
("mapc" . "∀")
|
||
("mapcar" . "∀")))
|
||
:config
|
||
(defun +modded/lisp-indent-function (indent-point state)
|
||
"This function is the normal value of the variable `lisp-indent-function'.
|
||
The function `calculate-lisp-indent' calls this to determine
|
||
if the arguments of a Lisp function call should be indented specially.
|
||
INDENT-POINT is the position at which the line being indented begins.
|
||
Point is located at the point to indent under (for default indentation);
|
||
STATE is the `parse-partial-sexp' state for that position.
|
||
If the current line is in a call to a Lisp function that has a non-nil
|
||
property `lisp-indent-function' (or the deprecated `lisp-indent-hook'),
|
||
it specifies how to indent. The property value can be:
|
||
,* `defun', meaning indent `defun'-style
|
||
\(this is also the case if there is no property and the function
|
||
has a name that begins with \"def\", and three or more arguments);
|
||
,* an integer N, meaning indent the first N arguments specially
|
||
(like ordinary function arguments), and then indent any further
|
||
arguments like a body;
|
||
,* a function to call that returns the indentation (or nil).
|
||
`lisp-indent-function' calls this function with the same two arguments
|
||
that it itself received.
|
||
This function returns either the indentation to use, or nil if the
|
||
Lisp function does not specify a special indentation."
|
||
(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))))))))
|
||
(add-hook 'emacs-lisp-mode-hook #'(lambda () (interactive) (setq-local lisp-indent-function #'+modded/lisp-indent-function))))
|
||
#+end_src
|