2637 lines
84 KiB
Org Mode
2637 lines
84 KiB
Org Mode
#+title: Emacs configuration
|
||
#+author: Aryadev Chavali
|
||
#+description: My new Emacs configuration
|
||
#+property: header-args:emacs-lisp :tangle config.el :comments link
|
||
#+options: toc:nil
|
||
|
||
#+begin_center
|
||
My configuration for vanilla Emacs
|
||
#+end_center
|
||
#+latex: \clearpage
|
||
|
||
#+toc: headlines
|
||
|
||
* 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
|
||
** Encoding
|
||
Set the encoding to utf-8-unix by default.
|
||
#+begin_src emacs-lisp
|
||
(use-package emacs
|
||
:straight nil
|
||
:init
|
||
(setq-default buffer-file-coding-system 'utf-8-unix
|
||
save-buffer-coding-system 'utf-8-unix))
|
||
#+end_src
|
||
** No littering
|
||
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 "personal-theme" theme which is stored in the elisp
|
||
folder (look at [[file:elisp/personal-theme.el][this file]]).
|
||
#+begin_src emacs-lisp
|
||
(use-package custom
|
||
:demand t
|
||
:straight nil
|
||
:init
|
||
(setq custom-theme-directory (concat user-emacs-directory "elisp/"))
|
||
:config
|
||
(load-theme 'personal t))
|
||
#+end_src
|
||
|
||
Set font size to 125.
|
||
#+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 prefer [[Dashboard]], and write into
|
||
the scratch buffer some nice information about Emacs.
|
||
#+begin_src emacs-lisp
|
||
(use-package startup
|
||
:straight nil
|
||
:defer t
|
||
:init
|
||
(setq inhibit-startup-screen t
|
||
initial-scratch-message (format ";; Emacs v%s\n" emacs-version)
|
||
ring-bell-function 'ignore))
|
||
#+end_src
|
||
|
||
Turn off blinking-cursor-mode as we will later be setting up hl-line,
|
||
which does a better job of indicating where the cursor is on screen.
|
||
#+begin_src emacs-lisp
|
||
(use-package frame
|
||
:straight nil
|
||
:config
|
||
(blink-cursor-mode 0))
|
||
#+end_src
|
||
** Path
|
||
Setting the path variable cos it can get annoying sometimes
|
||
#+begin_src emacs-lisp
|
||
(use-package env
|
||
:defer 1
|
||
:straight nil
|
||
:config
|
||
(setenv "PATH"
|
||
(concat
|
||
(expand-file-name "~/.local/bin:")
|
||
(getenv "PATH"))))
|
||
#+end_src
|
||
* Custom Functions
|
||
These are custom functions I have defined for various purposes.
|
||
** New line function
|
||
Vim doesn'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. Enter +dx/newline
|
||
|
||
|
||
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)
|
||
(save-excursion
|
||
(cond ((and BACKWARD (= BACKWARD 1)) (evil-open-below 1))
|
||
(t (evil-open-above 1))))
|
||
(evil-normal-state)))
|
||
#+end_src
|
||
** Toggle buffer
|
||
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 a buffer with that name. It then generates a
|
||
function with the given name that holds the necessary logic to
|
||
'toggle' buffers.
|
||
#+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
|
||
** Auto-run command after-save-hook
|
||
Macro that defines functionality that runs after a buffer save.
|
||
Requires a list of conditions (so it doesn't happen FOR ALL saved
|
||
buffers) and a function to run if the buffer satisfies the condition.
|
||
Then automatically adds it to the ~after-save-hook~.
|
||
|
||
On a Luke Smith video on YouTube, I saw a vim feature which I really
|
||
liked. He uses it in his DWM videos, but it's essentially an
|
||
"autocmd" call that, upon saving a certain file with vim, will run
|
||
a shell command (such as =make= or =pdflatex=).
|
||
|
||
It's arbitrary to program this functionality in Emacs. This macro
|
||
essentially performs the same task. In particular if you use
|
||
=start-process-shell-command= in the =to-run= function then you also
|
||
get a generated buffer of output from the command, useful for checking
|
||
errors and general messages. Better than vim, eh?
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package simple
|
||
:straight nil
|
||
:config
|
||
(defmacro +dx/create-auto-save (func-name conditions &rest to-run)
|
||
`(progn
|
||
(defun ,func-name ()
|
||
(interactive)
|
||
(when ,conditions
|
||
,@to-run))
|
||
(add-hook 'after-save-hook (quote ,func-name)))))
|
||
#+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 initial to set the initial value."
|
||
(let ((init (or initial 1)))
|
||
(if (= n 0)
|
||
init
|
||
(pow a (- n 1) (* a init)))))
|
||
#+end_src
|
||
** Define procedure
|
||
The =lambda= macro provides a function with possible arguments. A
|
||
procedure is a type of form that takes no 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 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.
|
||
#+begin_src emacs-lisp
|
||
(use-package general
|
||
:demand t
|
||
:config
|
||
(general-def
|
||
:states '(normal motion)
|
||
"SPC" nil
|
||
"M-V" #'+dx/newline
|
||
"M-v" (proc (interactive) (+dx/newline 1)))
|
||
|
||
(general-create-definer leader
|
||
:states '(normal motion)
|
||
:keymaps 'override
|
||
:prefix "SPC")
|
||
|
||
(general-create-definer local-leader
|
||
:states '(normal motion)
|
||
: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 emacs
|
||
:straight nil
|
||
:general
|
||
(general-def
|
||
"C-x d" #'delete-frame)
|
||
(general-def
|
||
:states '(normal motion)
|
||
"C--" #'text-scale-decrease
|
||
"C-=" #'text-scale-increase)
|
||
(leader
|
||
"SPC" #'execute-extended-command
|
||
"u" #'universal-argument
|
||
";" #'eval-expression
|
||
"!" #'async-shell-command
|
||
"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))
|
||
#+end_src
|
||
** Evil
|
||
Evil (Emacs VI Layer) is a package that brings the Vi experience to
|
||
Emacs. Packaged with it by default are:
|
||
- The modal system
|
||
- EX
|
||
- Vi mapping functions
|
||
|
||
This provides a lot of stuff for the average vim user moving to Emacs.
|
||
However there are many other packages surrounding evil that port even
|
||
greater functionality from vi to Emacs. Surround, commenting,
|
||
multiple cursors and further support to other packages are configured
|
||
here.
|
||
*** Evil Core
|
||
Setup the evil package, with some opinionated keybindings:
|
||
|
||
- Switch =evil-upcase= and =evil-downcase= cos I'd rather have the
|
||
non-capital one be upcase due to how much I use it
|
||
- Switch =evil-goto-mark= and =evil-goto-mark-line= as I'd rather have
|
||
the global one closer to the home row
|
||
#+begin_src emacs-lisp
|
||
(use-package evil
|
||
:hook (after-init-hook . evil-mode)
|
||
:general
|
||
(general-def
|
||
:states '(normal motion)
|
||
"TAB" #'evil-jump-item
|
||
"r" #'evil-replace-state
|
||
"zC" #'hs-hide-level
|
||
"'" #'evil-goto-mark
|
||
"`" #'evil-goto-mark-line
|
||
"C-w" #'evil-window-map
|
||
"gu" #'evil-upcase
|
||
"gU" #'evil-downcase)
|
||
(general-def
|
||
:states 'visual
|
||
:keymaps '(emacs-lisp-mode-map lisp-interaction-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
|
||
evil-want-abbrev-expand-on-insert-exit t
|
||
evil-undo-system 'undo-tree)
|
||
:config
|
||
(fset #'evil-window-vsplit #'make-frame))
|
||
#+end_src
|
||
*** Evil surround
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-surround
|
||
:after evil
|
||
:config
|
||
(global-evil-surround-mode))
|
||
#+end_src
|
||
*** Evil commentary
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-commentary
|
||
: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'. 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
|
||
: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 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
|
||
:after evil
|
||
:config
|
||
(evil-collection-require 'dired))
|
||
#+end_src
|
||
|
||
** Completion
|
||
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 (progressive input
|
||
filtering). 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. This is wrong: 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.
|
||
|
||
Along with frameworks, there is a configuration for the
|
||
completions-list, which is actually the original and default method of
|
||
completion within Emacs. When you first install Emacs without a
|
||
config, any 'completing-read' function leverages the completions-list when
|
||
=TAB= is used.
|
||
|
||
Though I believe Ido is a better completion system than the
|
||
completions-list, it still has it's place and can be used in tandem
|
||
with ido.
|
||
*** 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 keybind when
|
||
looking for a command.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package amx
|
||
:config
|
||
(amx-mode))
|
||
#+end_src
|
||
*** Orderless
|
||
Orderless sorting method for completion, probably one of the best
|
||
things ever.
|
||
#+begin_src emacs-lisp
|
||
(use-package orderless
|
||
:after (ivy ido))
|
||
#+end_src
|
||
*** Ido
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
Ido is a very old completion package that still works great to this
|
||
day. Though it is limited in its scope (and may thus be called a
|
||
completion add-on rather than a full on framework), it is still a very
|
||
powerful package. With the use of ido-completing-read+, it may be used
|
||
similarly to a fully fledged completion framework.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package ido
|
||
:demand t
|
||
:general
|
||
(general-def
|
||
:keymaps '(ido-buffer-completion-map
|
||
ido-file-completion-map
|
||
ido-file-dir-completion-map
|
||
ido-common-completion-map)
|
||
(kbd "M-j") #'ido-next-match
|
||
(kbd "M-k") #'ido-prev-match
|
||
(kbd "C-x o") #'evil-window-up)
|
||
:init
|
||
(setq ido-decorations
|
||
(list "{" "}" " \n" " ..." "[" "]" " [No match]" " [Matched]"
|
||
" [Not readable]" " [Too big]" " [Confirm]")
|
||
completion-styles '(flex partial-completion intials emacs22))
|
||
(setq-default ido-enable-flex-matching t
|
||
ido-enable-dot-prefix t
|
||
ido-enable-regexp nil)
|
||
(with-eval-after-load "magit"
|
||
(setq magit-completing-read-function 'magit-ido-completing-read))
|
||
:config
|
||
(ido-mode)
|
||
(ido-everywhere))
|
||
#+end_src
|
||
**** Ido ubiquitous
|
||
Ido completing-read+ is a package that extends the ido package to work
|
||
with more text based functions.
|
||
#+begin_src emacs-lisp
|
||
(use-package ido-completing-read+
|
||
:after ido
|
||
:config
|
||
(ido-ubiquitous-mode +1))
|
||
#+end_src
|
||
*** Completions-list
|
||
#+begin_src emacs-lisp
|
||
(use-package simple
|
||
:straight nil
|
||
:general
|
||
(general-def
|
||
:keymaps 'completion-list-mode-map
|
||
:states '(normal motion)
|
||
"l" #'next-completion
|
||
"h" #'previous-completion
|
||
"ESC" #'delete-completion-window
|
||
"q" #'quit-window
|
||
"RET" #'choose-completion)
|
||
:config
|
||
(with-eval-after-load "evil"
|
||
(setq evil-emacs-state-modes (cl-remove-if
|
||
#'(lambda (x) (eq 'completions-list-mode x))
|
||
evil-emacs-state-modes))
|
||
(add-to-list 'evil-normal-state-modes 'completions-list-mode)))
|
||
#+end_src
|
||
*** 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.
|
||
**** 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
|
||
:commands +org/swiper-goto
|
||
:general
|
||
(leader
|
||
"ss" #'counsel-grep-or-swiper
|
||
"sr" #'counsel-rg
|
||
"fr" #'counsel-recentf
|
||
"ic" #'counsel-unicode-char)
|
||
:init
|
||
(general-def
|
||
[remap describe-bindings] #'counsel-descbinds
|
||
[remap load-theme] #'counsel-load-theme)
|
||
:config
|
||
(setq ivy-initial-inputs-alist nil
|
||
counsel-describe-function-function #'helpful-callable
|
||
counsel-describe-variable-function #'helpful-variable
|
||
ivy-re-builders-alist '((swiper . ivy--regex-plus)
|
||
(counsel-grep-or-swiper . ivy--regex-plus)
|
||
(counsel-rg . ivy--regex-plus)
|
||
(t . orderless-ivy-re-builder)))
|
||
(with-eval-after-load "org-mode"
|
||
(general-def
|
||
[remap org-goto] #'counsel-org-goto))
|
||
(counsel-mode))
|
||
#+end_src
|
||
**** Ivy 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).
|
||
#+begin_src emacs-lisp
|
||
(use-package ivy
|
||
:hook (after-init-hook . ivy-mode)
|
||
:general
|
||
(general-def
|
||
:keymaps 'ivy-minibuffer-map
|
||
"C-j" #'ivy-yank-symbol
|
||
"M-j" #'ivy-next-line-or-history
|
||
"M-k" #'ivy-previous-line-or-history
|
||
"C-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 "amx"
|
||
(setq amx-backend 'ivy)))
|
||
#+end_src
|
||
**** Counsel etags
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
Counsel etags allows me to search generated tag files for tags. I
|
||
already have a function defined to generate the tags, so it's just
|
||
searching them which I find to be a bit of a hassle, and where this
|
||
package comes in.
|
||
#+begin_src emacs-lisp
|
||
(use-package counsel-etags
|
||
:after counsel
|
||
:general
|
||
(leader "st" #'counsel-etags-find-tag))
|
||
#+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 '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 the logical symbol for [[https://en.wikipedia.org/wiki/Universal_quantification][universal
|
||
quantification]]. Though this may seem like useless eye candy, it has
|
||
aided my comprehension and 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
|
||
(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)
|
||
(let ((arg args)
|
||
forms)
|
||
(while arg
|
||
(let* ((carg (car arg))
|
||
(mode (car carg))
|
||
(rest (cdr carg)))
|
||
(add-to-list
|
||
'forms
|
||
`(add-hook
|
||
',mode
|
||
(lambda ()
|
||
(setq prettify-symbols-alist ',rest)
|
||
(prettify-symbols-mode)))))
|
||
(setq arg (cdr arg)))
|
||
forms))))
|
||
|
||
(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. Of course, as Emacs is a powerful lisp
|
||
interpreter, this is easily manageable.
|
||
|
||
Here I create a few use-package extensions that manages the whole
|
||
ordeal of adding a new record to the display-buffer-alist, a useful
|
||
abstraction that makes it easy to manage the various buffers created
|
||
by packages.
|
||
#+begin_src emacs-lisp
|
||
(use-package window
|
||
:straight nil
|
||
:defer t
|
||
:general
|
||
(leader
|
||
:infix "b"
|
||
"b" #'switch-to-buffer
|
||
"K" #'kill-buffer
|
||
"j" #'next-buffer
|
||
"k" #'previous-buffer)
|
||
:init
|
||
(with-eval-after-load "use-package-core"
|
||
(add-to-list 'use-package-keywords ':display)
|
||
(defun use-package-normalize/:display (_name-symbol _keyword args)
|
||
args)
|
||
|
||
(defun use-package-handler/:display (name _keyword args rest state)
|
||
(use-package-concat
|
||
(use-package-process-keywords name rest state)
|
||
(let ((arg args)
|
||
forms)
|
||
(while arg
|
||
(add-to-list 'forms
|
||
`(add-to-list 'display-buffer-alist
|
||
',(car arg)))
|
||
(setq arg (cdr arg)))
|
||
forms)))))
|
||
#+end_src
|
||
*** Setup default display records
|
||
Using the =:display= keyword, setup up some =display-buffer-alist=
|
||
records.
|
||
#+begin_src emacs-lisp
|
||
(use-package window
|
||
:straight nil
|
||
:defer t
|
||
:display
|
||
("\\*\\(Wo\\)?Man.*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
|
||
("\\*Process List\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
|
||
("\\*compilation\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
|
||
("\\*\\(Ido \\)?Completions\\*"
|
||
(display-buffer-in-side-window)
|
||
(window-height . 0.25)
|
||
(side . bottom))
|
||
|
||
("\\*Async Shell Command\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25)))
|
||
#+end_src
|
||
** Auto typing
|
||
Snippets are a system by which pieces of code can be inserted via
|
||
prefixes. For example, an 'if' snippet would work by first inserting
|
||
the word 'if' then pressing some _expansion key_ such as TAB. This
|
||
will insert a set of text that may be have some components that need
|
||
to be further filled by the user.
|
||
|
||
The popular 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.
|
||
|
||
However, Emacs provides its own 'auto typing' facilities. Abbrevs and
|
||
skeletons make up the popular solution within Emacs default. Abbrevs
|
||
are for simple expressions wherein there is only one user input (say,
|
||
getting today's time which only requires you asking for it). They
|
||
provide a lot of inbuilt functionality and are quite useful.
|
||
Skeletons, on the other hand, are for higher level insertions
|
||
*** Abbrevs
|
||
Just define a few abbrevs for various date-time operations. Also
|
||
define a macro that will assume a function for the expansion, helping
|
||
with abstracting a few things away.
|
||
#+begin_src emacs-lisp
|
||
(use-package abbrev
|
||
:straight nil
|
||
:hook
|
||
(prog-mode-hook . abbrev-mode)
|
||
(text-mode-hook . abbrev-mode)
|
||
:init
|
||
(defmacro +autotyping/deff-abbrev (ABBREV-TABLE ABBREV EXPANSION)
|
||
"Wraps around define-abbrev to fill in some repeated stuff
|
||
when expansion is a function."
|
||
`(define-abbrev
|
||
,ABBREV-TABLE
|
||
,ABBREV
|
||
""
|
||
(proc (insert ,EXPANSION))))
|
||
|
||
(setq save-abbrevs nil)
|
||
:config
|
||
(+autotyping/deff-abbrev
|
||
global-abbrev-table
|
||
"sdate"
|
||
(format-time-string "%Y-%m-%d" (current-time)))
|
||
|
||
(+autotyping/deff-abbrev
|
||
global-abbrev-table
|
||
"stime"
|
||
(format-time-string "%H:%M:%S" (current-time)))
|
||
|
||
(+autotyping/deff-abbrev
|
||
text-mode-abbrev-table
|
||
"sday"
|
||
(format-time-string "%A" (current-time)))
|
||
|
||
(+autotyping/deff-abbrev
|
||
text-mode-abbrev-table
|
||
"smon"
|
||
(format-time-string "%B" (current-time))))
|
||
#+end_src
|
||
*** Skeletons
|
||
Defining some basic skeletons and a macro to help generate an abbrev
|
||
as well.
|
||
#+begin_src emacs-lisp
|
||
(use-package skeleton
|
||
:straight nil
|
||
:after abbrev
|
||
:config
|
||
(defmacro +autotyping/gen-skeleton-abbrev (mode abbrev &rest skeleton)
|
||
(let* ((table (intern (concat (symbol-name mode) "-abbrev-table")))
|
||
(skeleton-name (intern (concat "+skeleton/" (symbol-name mode) "/" abbrev))))
|
||
`(progn
|
||
(define-skeleton
|
||
,skeleton-name
|
||
""
|
||
,@skeleton)
|
||
(define-abbrev ,table
|
||
,abbrev
|
||
""
|
||
',skeleton-name)))))
|
||
#+end_src
|
||
*** Auto insert
|
||
#+begin_src emacs-lisp
|
||
(use-package autoinsert
|
||
:straight nil
|
||
:hook (after-init-hook . auto-insert-mode)
|
||
:config
|
||
(add-to-list
|
||
'auto-insert-alist
|
||
'(("\\.html\\'" . "HTML Skeleton")
|
||
""
|
||
"<!doctype html>
|
||
<html class='no-js' 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
|
||
*** 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
|
||
"ii" #'yas-insert-snippet)
|
||
:config
|
||
(yas-load-directory (no-littering-expand-etc-file-name "yasnippet/snippets")))
|
||
#+end_src
|
||
** Emacs Mode-line
|
||
:PROPERTIES:
|
||
:header-args:emacs-lisp: :tangle no
|
||
:END:
|
||
|
||
Check out [[*Telephone-line][this package]] for my current modeline.
|
||
|
||
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
|
||
** Telephone-line
|
||
Telephone-line is a mode-line package for Emacs which prioritises
|
||
extensibility. It also looks much nicer than the default mode line
|
||
with colouring and a ton of presentations to choose from.
|
||
#+begin_src emacs-lisp
|
||
(use-package telephone-line
|
||
:init
|
||
(defface +telephone/position-face '((t (:foreground "red" :background "grey10"))) "")
|
||
(defface +telephone/mode-face '((t (:foreground "white" :background "dark green"))) "")
|
||
(defface +telephone/file-info-face '((t (:foreground "white" :background "Dark Blue"))) "")
|
||
:custom
|
||
(telephone-line-faces
|
||
'((evil . telephone-line-modal-face)
|
||
(modal . telephone-line-modal-face)
|
||
(ryo . telephone-line-ryo-modal-face)
|
||
(accent . (telephone-line-accent-active . telephone-line-accent-inactive))
|
||
(nil . (mode-line . mode-line-inactive))
|
||
(position . (+telephone/position-face . mode-line-inactive))
|
||
(mode . (+telephone/mode-face . mode-line-inactive))
|
||
(file-info . (+telephone/file-info-face . mode-line-inactive))))
|
||
(telephone-line-primary-left-separator 'telephone-line-halfcos-left)
|
||
(telephone-line-secondary-left-separator 'telephone-line-halfcos-hollow-left)
|
||
(telephone-line-primary-right-separator 'telephone-line-identity-right)
|
||
(telephone-line-secondary-right-separator 'telephone-line-identity-hollow-right)
|
||
(telephone-line-height 24)
|
||
(telephone-line-evil-use-short-tag nil)
|
||
:config
|
||
(telephone-line-defsegment +telephone/buffer-or-filename ()
|
||
(cond
|
||
((buffer-file-name)
|
||
(if (and (fboundp 'projectile-project-name)
|
||
(fboundp 'projectile-project-p)
|
||
(projectile-project-p))
|
||
(list ""
|
||
(funcall (telephone-line-projectile-segment) face)
|
||
(propertize
|
||
(concat "/"
|
||
(file-relative-name (file-truename (buffer-file-name))
|
||
(projectile-project-root)))
|
||
'help-echo (buffer-file-name)))
|
||
(buffer-file-name)))
|
||
(t (buffer-name))))
|
||
|
||
(telephone-line-defsegment +telephone/get-position ()
|
||
`(,(concat "%lL:%cC"
|
||
(if (not mark-active)
|
||
""
|
||
(format " | %dc" (- (+ 1 (region-end)) (region-beginning)))))))
|
||
|
||
(setq-default
|
||
telephone-line-lhs '((mode telephone-line-major-mode-segment)
|
||
(file-info telephone-line-input-info-segment)
|
||
(position +telephone/get-position)
|
||
(accent +telephone/buffer-or-filename
|
||
telephone-line-process-segment))
|
||
telephone-line-rhs '((accent telephone-line-flycheck-segment telephone-line-misc-info-segment
|
||
telephone-line-projectile-segment)
|
||
(file-info telephone-line-filesize-segment)
|
||
(evil telephone-line-evil-tag-segment)))
|
||
(telephone-line-mode))
|
||
#+end_src
|
||
* Small packages
|
||
** ISearch
|
||
#+begin_src emacs-lisp
|
||
(use-package isearch
|
||
:straight nil
|
||
:general
|
||
(:keymaps 'isearch-mode-map
|
||
"M-s" #'isearch-repeat-forward))
|
||
#+end_src
|
||
** Display line numbers
|
||
I don't like using this mode by default, but I'd like to configure it
|
||
if possible. Line numbers are a necessary evil a lot of times, and
|
||
it's useful for presentations.
|
||
#+begin_src emacs-lisp
|
||
(use-package display-line-numbers
|
||
:straight nil
|
||
:defer t
|
||
:commands display-line-numbers-mode
|
||
:general
|
||
(leader
|
||
"tl" #'display-line-numbers-mode)
|
||
:init
|
||
(setq-default display-line-numbers-type 'relative))
|
||
#+end_src
|
||
** Hl-line
|
||
Hl-line is a
|
||
#+begin_src emacs-lisp
|
||
(use-package hl-line
|
||
:defer t
|
||
:hook (text-mode-hook . hl-line-mode))
|
||
#+end_src
|
||
** Recentf
|
||
Recentf makes it easy to
|
||
#+begin_src emacs-lisp
|
||
(use-package recentf
|
||
:straight nil
|
||
:hook (emacs-startup-hook . recentf-mode))
|
||
#+end_src
|
||
** 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 (emacs-startup-hook . projectile-mode)
|
||
:general
|
||
(leader "p" #'projectile-command-map)
|
||
:init
|
||
(setq projectile-tags-command "ctags -Re -f \"%s\" %s \"%s\""))
|
||
#+end_src
|
||
*** Counsel projectile
|
||
Counsel projectile provides the ivy interface to projectile commands, which is really useful.
|
||
#+begin_src emacs-lisp
|
||
(use-package counsel-projectile
|
||
:after (projectile counsel)
|
||
:config
|
||
(counsel-projectile-mode +1))
|
||
#+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
|
||
:after evil
|
||
:general
|
||
(leader
|
||
:infix "s"
|
||
"l" #'avy-goto-line)
|
||
(general-def
|
||
:states '(normal motion)
|
||
(kbd "C-s") #'avy-goto-char-timer
|
||
(kbd "M-s") #'isearch-forward))
|
||
#+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 motion)
|
||
[remap evil-window-next] #'ace-window))
|
||
#+end_src
|
||
** Helpful
|
||
Basic setup that replaces commands and configures
|
||
=display-buffer-alist= for helpful.
|
||
#+begin_src emacs-lisp
|
||
(use-package helpful
|
||
:commands (helpful-callable helpful-variable)
|
||
:general
|
||
(general-def
|
||
[remap describe-function] #'helpful-callable
|
||
[remap describe-variable] #'helpful-variable
|
||
[remap describe-key] #'helpful-key)
|
||
:display
|
||
("\\*[Hh]elp.*"
|
||
(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
|
||
Pretty simple, just activate after init.
|
||
#+begin_src emacs-lisp
|
||
(use-package which-key
|
||
: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
|
||
** mwim
|
||
Nice package for nicer movements. Of course the movements it enables
|
||
aren't really difficult to execute via Evil, but when running through
|
||
a large codebase or particularly long files with loads of lines of
|
||
code it does make life a bit easier.
|
||
#+begin_src emacs-lisp
|
||
(use-package mwim
|
||
:after keychord
|
||
:straight t
|
||
:general
|
||
(general-def
|
||
:states '(normal motion)
|
||
"gl" #'mwim-end
|
||
"gh" #'mwim-beginning))
|
||
#+end_src
|
||
** unicode-emoticons
|
||
If I want some cool emoticons to seem hip on my README, I'll use this.
|
||
#+begin_src emacs-lisp
|
||
(use-package unicode-emoticons
|
||
:straight t
|
||
:defer t
|
||
:general
|
||
(leader
|
||
"tE" #'unicode-emoticons-mode))
|
||
#+end_src
|
||
** (Rip)grep
|
||
Grep is likely one of the most important programs ever invented; a
|
||
must-have tool for any Linux users inventory. It is a searching
|
||
utility that allows one to search files for certain regex patterns.
|
||
The fact that there have been so many attempts to replace grep (with
|
||
limited success) only goes to show how important its general function
|
||
is to people.
|
||
|
||
Ripgrep is a grep-like utility written in Rust. It subsumes not only
|
||
the ability to search a given file but also to search multiple files
|
||
within a directory (which is usually only done by composing the
|
||
program find with grep to search multiple files). It is incredibly
|
||
fast by virtue of its regex optimisations and the use of ignore files
|
||
such as =.gitignore= to filter files when searching.
|
||
|
||
Grep has default Emacs utilities that use a =compilation= style buffer
|
||
to search a variety of differing data sets. =grep= searches files,
|
||
=rgrep= searches in a directory using the =find= binary and =zgrep=
|
||
searches archives. This is a great solution for most computer
|
||
environments as basically all of them will have grep and find
|
||
installed. Even when you =ssh= into a remote machine, they're likely
|
||
to have these tools.
|
||
|
||
The ripgrep package provides utilities to ripgrep projects and files
|
||
for strings via the rg binary. 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. Of
|
||
course, this requires installing the rg binary which is available in
|
||
most repositories nowadays.
|
||
*** Grep
|
||
#+begin_src emacs-lisp
|
||
(use-package grep
|
||
:display
|
||
("grep\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
:straight nil
|
||
:general
|
||
(leader
|
||
"sd" #'rgrep))
|
||
#+end_src
|
||
*** rg
|
||
#+begin_src emacs-lisp
|
||
(use-package rg
|
||
:defer t
|
||
:commands (+rg/search-in-new-frame)
|
||
:general
|
||
(leader
|
||
"sr" #'rg
|
||
"sR" #'+rg/search-in-new-frame)
|
||
(: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*")
|
||
:config
|
||
(defun +rg/search-in-new-frame ()
|
||
(interactive)
|
||
(let ((frame (make-frame)))
|
||
(select-frame frame)
|
||
(call-interactively #'rg))))
|
||
#+end_src
|
||
* Applications
|
||
** Dashboard
|
||
Dashboard creates a custom dashboard for Emacs that replaces the
|
||
initial startup screen in default Emacs.
|
||
#+begin_src emacs-lisp
|
||
(use-package dashboard
|
||
:straight t
|
||
:demand t
|
||
:general
|
||
(leader
|
||
"ab" #'dashboard-refresh-buffer)
|
||
(general-def
|
||
:states '(normal motion emacs)
|
||
:keymaps 'dashboard-mode-map
|
||
"q" (proc (interactive) (kill-this-buffer)))
|
||
(general-def
|
||
:states '(normal motion)
|
||
:keymaps 'dashboard-mode-map
|
||
"r" #'dashboard-jump-to-recent-files
|
||
"p" #'dashboard-jump-to-projects
|
||
"}" #'dashboard-next-section
|
||
"{" #'dashboard-previous-section)
|
||
:init
|
||
(setq initial-buffer-choice "*dashboard*"
|
||
dashboard-banner-logo-title "Oreomacs"
|
||
dashboard-center-content t
|
||
dashboard-set-init-info t
|
||
dashboard-startup-banner (no-littering-expand-etc-file-name "dashboard/logo.png")
|
||
dashboard-set-footer t
|
||
dashboard-set-navigator t
|
||
dashboard-items '((projects . 5)
|
||
(recents . 5)))
|
||
:config
|
||
(dashboard-setup-startup-hook))
|
||
#+end_src
|
||
** EWW
|
||
#+begin_src emacs-lisp
|
||
(use-package eww
|
||
:straight nil
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-eww-setup)))
|
||
#+end_src
|
||
** Calendar
|
||
Calendar is a simple inbuilt application within Emacs that helps with
|
||
date functionalities. I add functionality to copy dates from the
|
||
calendar to the kill ring and bind it to "Y".
|
||
#+begin_src emacs-lisp
|
||
(use-package calendar
|
||
:straight nil
|
||
:defer t
|
||
:commands (+calendar/copy-date +calendar/toggle-calendar)
|
||
:display
|
||
("\\*Calendar\\*"
|
||
(display-buffer-at-bottom)
|
||
(inhibit-duplicate-buffer . t)
|
||
(window-height . 0.17))
|
||
:general
|
||
(:keymaps 'calendar-mode-map
|
||
:states '(normal motion)
|
||
"Y" #'+calendar/copy-date)
|
||
(leader
|
||
"ad" #'+calendar/toggle-calendar)
|
||
:config
|
||
(defun +calendar/copy-date ()
|
||
"Copy date under cursor into kill ring."
|
||
(interactive)
|
||
(if (use-region-p)
|
||
(call-interactively #'kill-ring-save)
|
||
(let ((date (calendar-cursor-to-date)))
|
||
(when date
|
||
(setq date (encode-time 0 0 0 (nth 1 date) (nth 0 date) (nth 2 date)))
|
||
(kill-new (format-time-string "%Y-%m-%d" date))))))
|
||
(+dx/create-toggle-function +calendar/toggle-calendar "*Calendar*" calendar))
|
||
#+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
|
||
(defconst +mail/signature "---------------\nAryadev Chavali")
|
||
(defconst +mail/local-dir (concat user-emacs-directory ".mail/"))
|
||
|
||
(use-package notmuch
|
||
:commands notmuch
|
||
:general
|
||
(leader "am" #'notmuch)
|
||
:init
|
||
(defun +mail/sync-mail ()
|
||
"Sync mail via mbsync."
|
||
(interactive)
|
||
(start-process-shell-command "" nil "mbsync -a"))
|
||
(defun +mail/trash-junk ()
|
||
"Delete any mail in junk"
|
||
(interactive)
|
||
(start-process-shell-command "" nil "notmuch search --output=files --format=text0 tag:deleted tag:spam tag:trash tag:junk | xargs -r0 rm"))
|
||
:custom
|
||
(notmuch-show-logo nil)
|
||
(notmuch-search-oldest-first nil)
|
||
(notmuch-hello-sections '(notmuch-hello-insert-saved-searches notmuch-hello-insert-alltags))
|
||
(notmuch-archive-tags '("-inbox" "-unread" "+archive"))
|
||
(mail-signature +mail/signature)
|
||
(mail-default-directory +mail/local-dir)
|
||
(mail-source-directory +mail/local-dir)
|
||
(message-signature +mail/signature)
|
||
(message-auto-save-directory +mail/local-dir)
|
||
(message-directory +mail/local-dir)
|
||
:config
|
||
;; sync mail after refresh
|
||
(advice-add #'notmuch-poll-and-refresh-this-buffer :before
|
||
#'+mail/sync-mail)
|
||
(advice-add #'notmuch-poll-and-refresh-this-buffer :after
|
||
#'+mail/trash-junk)
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-notmuch-setup)))
|
||
#+end_src
|
||
*** Smtpmail
|
||
#+begin_src emacs-lisp
|
||
(use-package smtpmail
|
||
:after notmuch
|
||
:commands mail-send
|
||
:custom
|
||
(smtpmail-smtp-server "mail.aryadevchavali.com")
|
||
(smtpmail-smtp-user "aryadev")
|
||
(smtpmail-smtp-service 587)
|
||
(smtpmail-stream-type 'starttls)
|
||
:init
|
||
(setq send-mail-function #'smtpmail-send-it
|
||
message-send-mail-function #'smtpmail-send-it))
|
||
#+end_src
|
||
** Dired
|
||
Setup for dired. Make dired-hide-details-mode the default mode when
|
||
using dired-mode, as it removes the clutter. Setup evil collection
|
||
for dired (even though dired doesn't really conflict with evil, there
|
||
are some corners I'd like to adjust).
|
||
#+begin_src emacs-lisp
|
||
(use-package dired
|
||
:straight nil
|
||
:hook
|
||
(dired-mode-hook . dired-hide-details-mode)
|
||
(dired-mode-hook . auto-revert-mode)
|
||
:init
|
||
(setq-default dired-listing-switches "-AFBl --group-directories-first")
|
||
:general
|
||
(leader
|
||
:infix "d"
|
||
"f" #'find-dired
|
||
"D" #'dired-other-frame
|
||
"d" #'dired-jump
|
||
"l" (proc (interactive) (find-dired "~/Text/PDFs/" "-iname 'cs[0-9][0-9][0-9].pdf' -or -iname 'ma[0-9][0-9][0-9]*.pdf'")))
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-dired-setup))
|
||
(defun +dired/display-thumb-or-file ()
|
||
"If the file under point is a image file then display a
|
||
thumb, otherwise open the file."
|
||
(interactive)
|
||
(let* ((filename (dired-get-filename))
|
||
(ext (file-name-extension filename)))
|
||
(if (or (string= ext "png")
|
||
(string= ext "jpg")
|
||
(string= ext "jpeg")
|
||
(string= ext "gif"))
|
||
(image-dired-display-thumb)
|
||
(find-file-other-frame filename))))
|
||
|
||
(general-def
|
||
:states '(normal motion)
|
||
:keymaps 'dired-mode-map
|
||
"SPC" nil
|
||
"SPC ," nil)
|
||
|
||
(local-leader
|
||
:keymaps 'dired-mode-map
|
||
"l" #'dired-maybe-insert-subdir
|
||
"u" #'dired-undo
|
||
"i" #'+dired/display-thumb-or-file))
|
||
#+end_src
|
||
** Xwidget
|
||
Xwidget is a package (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 web pages which include JavaScript,
|
||
as it may come of use when doing web development. I can see the
|
||
results of work very quickly without switching windows or workspaces.
|
||
*** Xwidget Core
|
||
#+begin_src emacs-lisp
|
||
(use-package xwidget
|
||
:straight nil
|
||
:display
|
||
("\\*xwidget.*"
|
||
(display-buffer-pop-up-frame))
|
||
:general
|
||
(leader "au" #'xwidget-webkit-browse-url)
|
||
(general-def
|
||
:states '(normal motion)
|
||
:keymaps 'xwidget-webkit-mode-map
|
||
"q" #'quit-window
|
||
"h" #'xwidget-webkit-scroll-backward
|
||
"j" #'xwidget-webkit-scroll-up
|
||
"k" #'xwidget-webkit-scroll-down
|
||
"l" #'xwidget-webkit-scroll-forward
|
||
"+" #'xwidget-webkit-zoom-in
|
||
"-" #'xwidget-webkit-zoom-out
|
||
(kbd "C-f") #'xwidget-webkit-scroll-up
|
||
(kbd "C-b") #'xwidget-webkit-scroll-down
|
||
"H" #'xwidget-webkit-back
|
||
"L" #'xwidget-webkit-forward
|
||
"gu" #'xwidget-webkit-browse-url
|
||
"gr" #'xwidget-webkit-reload
|
||
"gg" #'xwidget-webkit-scroll-top
|
||
"G" #'xwidget-webkit-scroll-bottom))
|
||
#+end_src
|
||
*** Xwidget Extensions
|
||
Define a function =+xwidget/render-file= that reads a file name and
|
||
presents it in an xwidget. If the current file is an HTML file, ask
|
||
if user wants to open current file. Bind it to =aU= in the leader.
|
||
|
||
Also define a function =+xwidget/search-query= that first asks the
|
||
user what search engine they want to use ([[https://duckduckgo.com][Duck Duck Go]] and [[https://devdocs.io][DevDocs]]
|
||
currently) then asks for a query, which it parses then presents in an
|
||
xwidget window. Bind to =as= in the leader.
|
||
#+begin_src emacs-lisp
|
||
(use-package xwidget
|
||
:straight nil
|
||
:commands (+xwidget/render-file +xwidget/search)
|
||
:general
|
||
(leader
|
||
"aU" #'+xwidget/render-file
|
||
"as" #'+xwidget/search)
|
||
: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: "))))))
|
||
|
||
(defun +xwidget/search ()
|
||
"Run a search query on some search engine and display in
|
||
xwidget."
|
||
(interactive)
|
||
(let* ((engine (completing-read "Engine: " '("duckduckgo.com" "devdocs.io") nil t))
|
||
(query-raw (read-string "Enter query: "))
|
||
(query
|
||
(cond
|
||
((string= engine "duckduckgo.com") query-raw)
|
||
((string= engine "devdocs.io") (concat "_ " query-raw)))))
|
||
(xwidget-webkit-browse-url (concat "https://" engine "/?q=" query)))))
|
||
#+end_src
|
||
** Eshell
|
||
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.
|
||
|
||
*** Eshell Core
|
||
Setup a function that /toggles/ the eshell window rather than
|
||
just opening it via =+dx/toggle-buffer=.
|
||
Along with that setup the prompt so it looks a bit nicer and add
|
||
pretty symbols to eshell.
|
||
#+begin_src emacs-lisp
|
||
(use-package eshell
|
||
:commands +shell/toggle-shell
|
||
:display
|
||
("\\*e?shell\\*" ; for general shells as well
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
:pretty
|
||
(eshell-mode-hook
|
||
("lambda" . "λ")
|
||
("numberp" . "ℤ")
|
||
("t" . "⊤")
|
||
("nil" . "∅"))
|
||
:general
|
||
(leader
|
||
"tt" #'+shell/toggle-eshell)
|
||
:init
|
||
(add-hook
|
||
'eshell-mode-hook
|
||
(proc
|
||
(interactive)
|
||
(general-def
|
||
:states '(insert normal)
|
||
:keymaps 'eshell-mode-map
|
||
"M-l" (proc (interactive) (eshell/clear)
|
||
"M-j" #'eshell-next-matching-input-from-input
|
||
"M-k" #'eshell-previous-matching-input-from-input)
|
||
(local-leader
|
||
:keymaps 'eshell-mode-map
|
||
"c" (proc (interactive) (eshell/clear)
|
||
(recenter))
|
||
"k" #'eshell-kill-process))))
|
||
:config
|
||
(setq eshell-cmpl-ignore-case t
|
||
eshell-cd-on-directory t
|
||
eshell-prompt-function
|
||
(proc
|
||
(concat
|
||
(format "[%s]\n" (abbreviate-file-name (eshell/pwd)))
|
||
"λ "))
|
||
eshell-prompt-regexp "^λ ")
|
||
|
||
(+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 motion)
|
||
:keymaps 'elfeed-search-mode-map
|
||
"gr" #'elfeed-update
|
||
"s" #'elfeed-search-live-filter
|
||
"<return>" #'elfeed-search-show-entry)
|
||
:init
|
||
(setq elfeed-db-directory (no-littering-expand-var-file-name "elfeed/"))
|
||
(setq +rss/feed-urls
|
||
'(("Arch Linux"
|
||
"https://www.archlinux.org/feeds/news/"
|
||
Linux)
|
||
("LEMMiNO"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCRcgy6GzDeccI7dkbbBna3Q"
|
||
YouTube Stories)
|
||
("The Onion"
|
||
"https://www.theonion.com/rss"
|
||
Social)
|
||
("Dark Sominium"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UC_e39rWdkQqo5-LbiLiU10g"
|
||
YouTube Stories)
|
||
("Dark Sominium Music"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCkLiZ_zLynyNd5fd62hg1Kw"
|
||
YouTube Music)
|
||
("Nexpo"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCpFFItkfZz1qz5PpHpqzYBw"
|
||
YouTube)
|
||
("Techquickie"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UC0vBXGSyV14uvJ4hECDOl0Q"
|
||
YouTube)
|
||
("3B1B"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCYO_jab_esuFRV4b17AJtAw"
|
||
YouTube)
|
||
("Fredrik Knusden"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCbWcXB0PoqOsAvAdfzWMf0w"
|
||
YouTube Stories)
|
||
("Barely Sociable"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UC9PIn6-XuRKZ5HmYeu46AIw"
|
||
YouTube Stories)
|
||
("Atrocity Guide"
|
||
"https://www.youtube.com/feeds/videos.xml?channel_id=UCn8OYopT9e8tng-CGEWzfmw"
|
||
YouTube Stories)
|
||
("Hacker News"
|
||
"http://morss.aryadevchavali.com/news.ycombinator.com/rss"
|
||
Social)
|
||
("Hacker Factor"
|
||
"https://www.hackerfactor.com/blog/index.php?/feeds/index.rss2"
|
||
Social)
|
||
("BBC Top News"
|
||
"http://morss.aryadevchavali.com/feeds.bbci.co.uk/news/rss.xml"
|
||
News)
|
||
("BBC Tech News"
|
||
"http://morss.aryadevchavali.com/feeds.bbci.co.uk/news/technology/rss.xml"
|
||
News)))
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-elfeed-setup))
|
||
(setq elfeed-feeds (cl-map 'list #'(lambda (item)
|
||
(append (list (nth 1 item)) (cdr (cdr item))))
|
||
+rss/feed-urls)))
|
||
#+end_src
|
||
** Magit
|
||
Magit is *the* git porcelain for Emacs, which perfectly encapsulates
|
||
the git cli. In this case, I just need to setup the bindings for it.
|
||
As magit will definitely load after evil (as it must be run by a
|
||
binding, and evil will load after init), I can use evil-collection
|
||
freely. Also, define an auto insert for commit messages so that I
|
||
don't need to write everything myself.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package magit
|
||
: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-status)
|
||
:init
|
||
(setq vc-follow-symlinks t)
|
||
(with-eval-after-load "autoinsert"
|
||
(define-auto-insert '("COMMIT_EDITMSG" , "Commit")
|
||
'(nil
|
||
"(" (read-string "Enter feature/module: ") ")"
|
||
(read-string "Enter simple description: ") "\n\n"
|
||
_)))
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-magit-setup)))
|
||
#+end_src
|
||
** IBuffer
|
||
#+begin_src emacs-lisp
|
||
(use-package ibuffer
|
||
:general
|
||
(leader
|
||
"bi" #'ibuffer)
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-ibuffer-setup)))
|
||
#+end_src
|
||
** Processes
|
||
Emacs has two systems for process management:
|
||
+ proced: a general 'top' like interface which allows general
|
||
management of linux processes
|
||
+ list-processes: a specific Emacs based system that lists processes
|
||
spawned by Emacs (similar to a top for Emacs specifically)
|
||
|
||
*** Proced
|
||
Core proced config, just a few bindings and evil collection setup.
|
||
#+begin_src emacs-lisp
|
||
(use-package proced
|
||
:straight nil
|
||
:general
|
||
(leader
|
||
"ap" #'proced)
|
||
(general-def
|
||
:states 'normal
|
||
:keymaps 'proced-mode-map
|
||
"za" #'proced-toggle-auto-update)
|
||
:display
|
||
("\\*Proced\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
:init
|
||
(setq proced-auto-update-interval 0.5)
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-proced-setup)))
|
||
#+end_src
|
||
|
||
Along with that I setup the package =proced-narrow= which allows
|
||
further filtering of the process list.
|
||
#+begin_src emacs-lisp
|
||
(use-package proced-narrow
|
||
:straight t
|
||
:after proced
|
||
:general
|
||
(general-def
|
||
:states 'normal
|
||
:keymaps 'proced-mode-map
|
||
"%" #'proced-narrow))
|
||
#+end_src
|
||
** Calculator
|
||
Surprise, surprise Emacs comes with a calculator. At this point there
|
||
is little that surprises me in terms of Emacs' amazing capabilities.
|
||
|
||
=calc-mode= is a calculator system within Emacs that provides a
|
||
diverse array of mathematical operations. It uses reverse polish
|
||
notation to do calculations (though there is a standard infix
|
||
algebraic notation mode) and provides incredible utilities.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package calc
|
||
:straight nil
|
||
:display
|
||
("*Calculator*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.18))
|
||
:general
|
||
(leader
|
||
"ac" #'calc)
|
||
:init
|
||
(setq calc-algebraic-mode t)
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-calc-setup)))
|
||
#+end_src
|
||
*** Calctex
|
||
=calc-mode= also has a 3rd party package called =calctex=. It renders
|
||
mathematical expressions within calc as if they were rendered in TeX.
|
||
You can also copy the expressions in their TeX forms, which is pretty
|
||
useful when writing a paper. I've set a very specific lock on this
|
||
repository as it's got quite a messy work-tree and this commit seems to
|
||
work for me given the various TeX utilities installed via Arch.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package calctex
|
||
:after calc
|
||
:straight (calctex :type git :host github :repo "johnbcoughlin/calctex")
|
||
:hook (calc-mode-hook . calctex-mode))
|
||
#+end_src
|
||
** Ledger
|
||
#+begin_src emacs-lisp
|
||
(use-package ledger-mode
|
||
:defer t)
|
||
|
||
(use-package evil-ledger
|
||
:after ledger-mode)
|
||
#+end_src
|
||
* Major modes, programming and text
|
||
Setups for common major modes and languages.
|
||
** Text Configuration
|
||
Standard packages and configurations for the text-mode.
|
||
*** 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 motion)
|
||
:keymaps 'text-mode-map
|
||
(kbd "M-a") #'flyspell-correct-word-before-point
|
||
(kbd "M-A") #'flyspell-auto-correct-word))
|
||
#+end_src
|
||
*** Undo tree
|
||
Undo tree is a system for handling the history of any buffer. It
|
||
provides a very nice 'tree' visualiser (hence the name) for revisions
|
||
of a file or buffer, and allows you to move around different verisons
|
||
at once, without using a VCS like git (all in Emacs, baby).
|
||
#+begin_src emacs-lisp
|
||
(use-package undo-tree
|
||
:straight t
|
||
:hook (emacs-startup-hook . global-undo-tree-mode))
|
||
#+end_src
|
||
*** White space
|
||
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
|
||
:straight nil
|
||
:general
|
||
(general-def
|
||
:states '(normal motion)
|
||
"M--" #'whitespace-cleanup)
|
||
: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)
|
||
:init
|
||
(setq whitespace-style '(face lines-tail tabs tab-mark trailing newline)
|
||
whitespace-line-column 80))
|
||
#+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
|
||
*** 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
|
||
: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
|
||
** Programming Configuration
|
||
*** 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
|
||
:after project
|
||
:defer t
|
||
:hook
|
||
(c++-mode-hook . eglot-ensure)
|
||
(c-mode-hook . eglot-ensure)
|
||
(python-mode-hook . eglot-ensure)
|
||
:general
|
||
(leader
|
||
:keymaps 'eglot-mode-map
|
||
:infix "c"
|
||
"f" #'eglot-format
|
||
"a" #'eglot-code-actions
|
||
"r" #'eglot-rename
|
||
"R" #'eglot-reconnect)
|
||
;; :init
|
||
;; (setq eglot-stay-out-of '(flymake))
|
||
:config
|
||
(add-to-list 'eglot-server-programs '((c++-mode c-mode) "clangd"))
|
||
(add-to-list 'eglot-server-programs `(csharp-mode "~/.local/src/omnisharp-roslyn/run" "-lsp")))
|
||
#+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)
|
||
:display
|
||
("\\*Flycheck.*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
:config
|
||
(with-eval-after-load "evil-collection"
|
||
(evil-collection-flycheck-setup)))
|
||
#+end_src
|
||
*** Tabs and spaces
|
||
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 +dx/activate-tabs ()
|
||
(interactive)
|
||
(setq-local indent-tabs-mode t))
|
||
#+end_src
|
||
*** Colourising compilation
|
||
Colourising the compilation buffer so ansi color codes get computed.
|
||
#+begin_src emacs-lisp
|
||
(use-package compile
|
||
:defer t
|
||
: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
|
||
*** Highlight todo items
|
||
TODO items are highlighted in org buffers, but not necessarily in
|
||
every buffer. This minor mode highlights all TODO like items via a
|
||
list of strings to match. It also configures faces to use when
|
||
highlighting.
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package hl-todo
|
||
:after prog-mode
|
||
:hook (prog-mode-hook . hl-todo-mode)
|
||
:init
|
||
(setq hl-todo-keyword-faces
|
||
'(("TODO" . "#E50000")
|
||
("WAIT" . "#00CC00"))))
|
||
#+end_src
|
||
** PDF
|
||
PDFs are a format for (somewhat) immutable text and reports with great
|
||
formatting options. Though Emacs isn't my favourite application for
|
||
viewing PDFs (I highly recommend [[https://pwmt.org/projects/zathura/][Zathura]]), similar to most things with
|
||
Emacs, having a PDF viewer builtin can be a very useful asset.
|
||
|
||
For example if I were editing an org document which I was eventually
|
||
compiling into a PDF, my workflow would be much smoother with a PDF
|
||
viewer within Emacs that I can open on another pane.
|
||
|
||
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
|
||
:mode ("\\.[pP][dD][fF]" . pdf-view-mode)
|
||
:config
|
||
(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
|
||
:after pdf-tools
|
||
:hook (pdf-view-mode-hook . pdfgrep-mode)
|
||
:general
|
||
(general-def
|
||
:states 'normal
|
||
:keymaps 'pdf-view-mode-map
|
||
"M-g" #'pdfgrep))
|
||
#+end_src
|
||
** SQL
|
||
#+begin_src emacs-lisp
|
||
(use-package sql
|
||
:straight nil
|
||
:init
|
||
(setq sql-display-sqli-buffer-function nil))
|
||
#+end_src
|
||
** Ada
|
||
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 to do the important
|
||
stuff.
|
||
|
||
#+begin_src emacs-lisp
|
||
(load-file (concat user-emacs-directory "elisp/ada-mode.el"))
|
||
(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. Though I doubt many programmers nowadays are
|
||
wrangling with binary formats at such a precise level, I like to use
|
||
binary formats in my programs sometimes. 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, but I care
|
||
not to describe them. Check out the [[https://elpa.gnu.org/packages/nhexl-mode.html][page]] yourself.
|
||
#+begin_src emacs-lisp
|
||
(use-package nhexl-mode
|
||
:straight t
|
||
:mode "\\.bin")
|
||
#+end_src
|
||
** Org
|
||
*** Org Core Variables
|
||
Tons of variables for org-mode, including a ton of latex ones.
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:defer t
|
||
:custom
|
||
(org-agenda-files `(,(expand-file-name "~/Text")))
|
||
(org-agenda-window-setup 'current-window)
|
||
(org-edit-src-content-indentation 0)
|
||
(org-goto-interface 'outline)
|
||
(org-src-window-setup 'current-window)
|
||
(org-indirect-buffer-display 'current-window)
|
||
(org-export-backends '(ascii html latex odt))
|
||
(org-imenu-depth 10)
|
||
(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-babel-load-languages '((emacs-lisp . t)
|
||
(C . t)
|
||
(python . t)))
|
||
(org-latex-listings '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
|
||
'("%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")
|
||
)))
|
||
#+end_src
|
||
*** Org Core Configuration
|
||
Hooks, prettify-symbols and records for auto insertion.
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:hook
|
||
(org-mode-hook . prettify-symbols-mode)
|
||
:display
|
||
("\\*Org Src.*"
|
||
(display-buffer-same-window))
|
||
:pretty
|
||
(org-mode-hook
|
||
("#+begin_src" . "≫")
|
||
("#+end_src" . "≪"))
|
||
:init
|
||
(with-eval-after-load "autoinsert"
|
||
(define-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\n"
|
||
"* " _))))
|
||
#+end_src
|
||
*** Org Core Bindings
|
||
Some bindings for org mode.
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:general
|
||
(leader
|
||
"aa" #'org-agenda
|
||
"fa" (proc (interactive) (find-file (car org-agenda-files))))
|
||
(local-leader
|
||
:keymaps 'org-mode-map
|
||
"t" #'org-ctrl-c-ctrl-c
|
||
"l" #'org-latex-preview
|
||
"s" #'org-property-action
|
||
"e" #'org-export-dispatch))
|
||
#+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
|
||
:hook (message-mode-hook . org-msg-mode)
|
||
:after message-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 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).
|
||
#+begin_src emacs-lisp
|
||
(use-package org
|
||
:config
|
||
(defvar +org/compile-to-pdf-on-save-p
|
||
nil
|
||
"Non-nil to activate compile functionality.")
|
||
(+dx/create-auto-save
|
||
+org/compile-to-pdf-on-save-f
|
||
(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
|
||
*** Evil Org
|
||
Evil org for some nice bindings.
|
||
#+begin_src emacs-lisp
|
||
(use-package evil-org
|
||
:hook (org-mode-hook . evil-org-mode))
|
||
#+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
|
||
:after org
|
||
:init
|
||
(setq org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js"
|
||
org-reveal-theme "sky"))
|
||
#+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 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
|
||
** C/C++
|
||
Setup for C and C++ modes via the cc-mode package.
|
||
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.
|
||
*** Configuration
|
||
#+begin_src emacs-lisp
|
||
(use-package cc-mode
|
||
:defer t
|
||
:hook
|
||
(c-mode-hook . auto-fill-mode)
|
||
(c++-mode-hook . auto-fill-mode)
|
||
:pretty
|
||
(c-mode-hook
|
||
("puts" . "ℙ")
|
||
("fputs" . "ϕ")
|
||
("printf" . "ω")
|
||
("fprintf" . "Ω")
|
||
("->" . "→")
|
||
("NULL" . "∅")
|
||
("true" . "⊤")
|
||
("false" . "⊥")
|
||
("char" . "ℂ")
|
||
("int" . "ℤ")
|
||
("float" . "ℚ")
|
||
("double" . "ℝ")
|
||
("!" . "¬")
|
||
("&&" . "∧")
|
||
("||" . "∨")
|
||
("for" . "∀")
|
||
("return" . "⟼"))
|
||
(c++-mode-hook
|
||
("nullptr" . "∅")
|
||
("string" . "𝕊")
|
||
("vector" . "ℓ")
|
||
("puts" . "ℙ")
|
||
("fputs" . "ϕ")
|
||
("printf" . "ω")
|
||
("fprintf" . "Ω")
|
||
("->" . "→")
|
||
("NULL" . "∅")
|
||
("true" . "⊤")
|
||
("false" . "⊥")
|
||
("char" . "ℂ")
|
||
("int" . "ℤ")
|
||
("float" . "ℚ")
|
||
("double" . "ℝ")
|
||
("!" . "¬")
|
||
("&&" . "∧")
|
||
("||" . "∨")
|
||
("for" . "∀")
|
||
("return" . "⟼"))
|
||
:init
|
||
(setq-default c-basic-offset 2)
|
||
(setq-default c-default-style '((other . "user")))
|
||
|
||
(with-eval-after-load "autoinsert"
|
||
(define-auto-insert
|
||
'("\\.c\\'" . "C skeleton")
|
||
'(""
|
||
"/* " (file-name-nondirectory (buffer-file-name (current-buffer))) "\n"
|
||
" * Date: " (format-time-string "%Y-%m-%d") "\n"
|
||
" * Author: " user-full-name "\n"
|
||
" */\n"
|
||
"\n"
|
||
"\n"
|
||
_))
|
||
|
||
(define-auto-insert
|
||
'("\\.cpp\\'" . "C++ skeleton")
|
||
'(""
|
||
"/* " (file-name-nondirectory (buffer-file-name (current-buffer))) "\n"
|
||
" * Date: " (format-time-string "%Y-%m-%d") "\n"
|
||
" * Author: " user-full-name "\n"
|
||
" */\n"
|
||
"\n"
|
||
"\n"
|
||
_)))
|
||
:config
|
||
(c-add-style
|
||
"user"
|
||
'((c-basic-offset . 2)
|
||
(c-comment-only-line-offset . 0)
|
||
(c-hanging-braces-alist (brace-list-open)
|
||
(brace-entry-open)
|
||
(substatement-open after)
|
||
(block-close . c-snug-do-while)
|
||
(arglist-cont-nonempty))
|
||
(c-cleanup-list brace-else-brace)
|
||
(c-offsets-alist
|
||
(statement-block-intro . +)
|
||
(substatement-open . 0)
|
||
(access-label . -)
|
||
(inline-open . 0)
|
||
(label . 0)
|
||
(statement-cont . +)))))
|
||
#+end_src
|
||
*** Clang format
|
||
Clang format comes in built with clang, so download that before using
|
||
this. Formats C/C++ files depending on a format (checkout the Clang
|
||
format [[file:~/Dotfiles/ClangFormat/.clang-format][config file]] in my dotfiles).
|
||
|
||
#+begin_src emacs-lisp
|
||
(use-package clang-format
|
||
:straight nil
|
||
:load-path "/usr/share/clang/"
|
||
:after cc-mode
|
||
:commands +code/clang-format-region-or-buffer
|
||
:general
|
||
(leader
|
||
:keymaps '(c-mode-map c++-mode-map)
|
||
"cf" #'+code/clang-format-region-or-buffer)
|
||
:config
|
||
(add-hook 'after-save-hook (proc (interactive)
|
||
(if (or (eq major-mode 'c-mode)
|
||
(eq major-mode 'c++-mode))
|
||
(clang-format-buffer))))
|
||
(defun +code/clang-format-region-or-buffer ()
|
||
(interactive)
|
||
(if (mark)
|
||
(clang-format-region (region-beginning) (region-end))
|
||
(clang-format-buffer))))
|
||
#+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
|
||
:hook (racket-mode-hook . racket-xp-mode)
|
||
:display
|
||
("\\*Racket.*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
:general
|
||
(local-leader
|
||
:keymaps 'racket-mode-map
|
||
"r" #'racket-run
|
||
"i" #'racket-repl
|
||
"sr" #'racket-send-region
|
||
"sd" #'racket-send-definition))
|
||
#+end_src
|
||
** CSharp
|
||
#+begin_src emacs-lisp
|
||
(use-package csharp-mode
|
||
:defer t
|
||
:pretty
|
||
(csharp-mode-hook
|
||
("null" . "∅")
|
||
("string" . "𝕊")
|
||
("List" . "ℓ")
|
||
("WriteLine" . "ℙ")
|
||
("Write" . "ω")
|
||
("->" . "→")
|
||
("true" . "⊤")
|
||
("false" . "⊥")
|
||
("char" . "ℂ")
|
||
("int" . "ℤ")
|
||
("float" . "ℝ")
|
||
("!" . "¬")
|
||
("&&" . "∧")
|
||
("||" . "∨")
|
||
("for" . "∀")
|
||
("return" . "⟼")))
|
||
#+end_src
|
||
** Java
|
||
#+begin_src emacs-lisp
|
||
(use-package ob-java
|
||
:straight nil
|
||
:pretty
|
||
(java-mode-hook
|
||
("println" . "ℙ")
|
||
("printf" . "ω")
|
||
("null" . "∅")
|
||
("true" . "⊤")
|
||
("false" . "⊥")
|
||
("char" . "ℂ")
|
||
("int" . "ℤ")
|
||
("float" . "ℝ")
|
||
("!" . "¬")
|
||
("&&" . "∧")
|
||
("||" . "∨")
|
||
("for" . "∀")
|
||
("return" . "⟼"))
|
||
:config
|
||
(with-eval-after-load "cc-mode"
|
||
(c-add-style
|
||
"java"
|
||
'((c-basic-offset . 4)
|
||
(c-comment-only-line-offset 0 . 0)
|
||
(c-offsets-alist
|
||
(inline-open . 0)
|
||
(topmost-intro-cont . +)
|
||
(statement-block-intro . +)
|
||
(knr-argdecl-intro . 5)
|
||
(substatement-open . 0)
|
||
(substatement-label . +)
|
||
(label . +)
|
||
(statement-case-open . +)
|
||
(statement-cont . +)
|
||
(arglist-intro . c-lineup-arglist-intro-after-paren)
|
||
(arglist-close . c-lineup-arglist)
|
||
(brace-list-intro first c-lineup-2nd-brace-entry-in-arglist c-lineup-class-decl-init-+ +)
|
||
(access-label . 0)
|
||
(inher-cont . c-lineup-java-inher)
|
||
(func-decl-cont . c-lineup-java-throws))))
|
||
(add-to-list 'c-default-style '(java-mode . "java")))
|
||
|
||
(with-eval-after-load "abbrev"
|
||
(define-abbrev-table 'java-mode-abbrev-table nil)
|
||
(add-hook 'java-mode-hook
|
||
(proc (setq-local local-abbrev-table java-mode-abbrev-table)))))
|
||
#+end_src
|
||
** Haskell
|
||
Haskell is a static lazy functional programming language (what a
|
||
mouthful). It's quite a beautiful language and really learning it will
|
||
change the way you think about programming.
|
||
|
||
Here I configure the REPL for Haskell via the
|
||
=haskell-interactive-mode= as well.
|
||
#+begin_src emacs-lisp
|
||
(use-package haskell-mode
|
||
:hook
|
||
(haskell-mode-hook . haskell-indentation-mode)
|
||
(haskell-mode-hook . interactive-haskell-mode)
|
||
:custom
|
||
(haskell-interactive-prompt "[λ] ")
|
||
(haskell-interactive-prompt-cont "{λ} ")
|
||
(haskell-interactive-popup-errors nil)
|
||
(haskell-stylish-on-save nil)
|
||
(haskell-process-type 'stack-ghci)
|
||
:general
|
||
(leader
|
||
"th" #'+shell/toggle-haskell-repl)
|
||
:display
|
||
("\\*haskell.**\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
:config
|
||
(load (concat user-emacs-directory "elisp/haskell-multiedit.el"))
|
||
(+dx/create-toggle-function
|
||
+shell/toggle-haskell-repl
|
||
"*haskell*"
|
||
haskell-interactive-bring))
|
||
#+end_src
|
||
** Python
|
||
Basic, haven't used python in this configuration yet.
|
||
#+begin_src emacs-lisp
|
||
(use-package python
|
||
:defer t
|
||
:straight nil
|
||
:pretty
|
||
(python-mode-hook
|
||
("None" . "∅")
|
||
("list" . "ℓ")
|
||
("List" . "ℓ")
|
||
("str" . "𝕊")
|
||
("True" . "⊤")
|
||
("False" . "⊥")
|
||
("int" . "ℤ")
|
||
("float" . "ℝ")
|
||
("not" . "¬")
|
||
("and" . "∧")
|
||
("or" . "∨")
|
||
("for" . "∀")
|
||
("print" . "ℙ")
|
||
("lambda" . "λ")
|
||
("return" . "⟼")
|
||
("yield" . "⟻"))
|
||
: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
|
||
:commands +python/toggle-repl
|
||
:general
|
||
(leader
|
||
"tp" #'+shell/python-toggle-repl)
|
||
:display
|
||
("\\*Python\\*"
|
||
(display-buffer-at-bottom)
|
||
(window-height . 0.25))
|
||
:config
|
||
(+dx/create-toggle-function
|
||
+shell/python-toggle-repl
|
||
"*Python*"
|
||
run-python))
|
||
#+end_src
|
||
** YAML
|
||
YAML is a data language which is useful for config files.
|
||
#+begin_src emacs-lisp
|
||
(use-package yaml-mode
|
||
:straight t)
|
||
#+end_src
|
||
** HTML/CSS/JS
|
||
Firstly, web mode for consistent colouring of syntax.
|
||
#+begin_src emacs-lisp
|
||
(use-package web-mode
|
||
:mode ("\\.html" . web-mode)
|
||
:mode ("\\.js" . web-mode)
|
||
:mode ("\\.css" . web-mode)
|
||
:custom
|
||
((web-mode-code-indent-offset 2)
|
||
(web-mode-markup-indent-offset 2)
|
||
(web-mode-css-indent-offset 2)))
|
||
#+end_src
|
||
|
||
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
|
||
** Typescript
|
||
#+begin_src emacs-lisp
|
||
(use-package typescript-mode)
|
||
#+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
|
||
:pretty
|
||
(emacs-lisp-mode-hook
|
||
("lambda" . "λ")
|
||
("numberp" . "ℤ")
|
||
("t" . "⊤")
|
||
("nil" . "∅")
|
||
("and" . "∧")
|
||
("or" . "∨")
|
||
("defun" . "ƒ")
|
||
("for" . "∀")
|
||
("mapc" . "∀")
|
||
("mapcar" . "∀"))
|
||
:config
|
||
(defun +dx/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))))))))
|
||
(add-hook 'emacs-lisp-mode-hook (proc (interactive) (setq-local lisp-indent-function #'+dx/lisp-indent-function))))
|
||
#+end_src
|