And also some Org mode settings for indenting stuff.
15 KiB
General Programming Configuration
A literate programming file for helping me program.
Introduction
Seems that all programming interfaces and workflows behave similarly. However…
General
The following work for all programming languages.
direnv
Farm off commands into virtual environments:
(use-package direnv
:init
(setq direnv--executable "/usr/local/bin/direnv"))
Function Call Notifications
As I've mentioned on my website, I've created a beep function that notifies when long running processes complete.
(use-package alert)
(use-package beep
:straight nil)
While that code advices the publishing and compile commands, I may want to add more.
iEdit
While there are language-specific ways to rename variables and functions, iedit
is often sufficient.
(ha-leader "s e" '("iedit" . iedit-mode))
Commenting
I like comment-dwim
(M-;
), and I like comment-box
, but I have an odd personal style that I like to codify:
(defun ha-comment-line (&optional start end)
(interactive "r")
(when (or (null start) (not (region-active-p)))
(setq start (line-beginning-position))
(setq end (line-end-position)))
(save-excursion
(narrow-to-region start end)
(upcase-region start end)
(goto-char (point-min))
(insert "------------------------------------------------------------------------\n")
(goto-char (point-max))
(insert "\n------------------------------------------------------------------------")
(comment-region (point-min) (point-max))
(widen)))
And a keybinding:
(general-evil-define-key 'normal prog-mode-map
:prefix "SPC m"
"c" '("comment line" . ha-comment-line))
Evaluation
Typical keybindings for all programming modes:
(general-evil-define-key 'normal prog-mode-map
:prefix "SPC m"
"e" '(:ignore t :which-key "eval")
"e ;" '("expression" . eval-expression)
"e b" '("buffer" . eval-buffer)
"e f" '("function" . eval-defun)
"e r" '("region" . eval-region)
"e e" '("last s-exp" . eval-last-sexp)
"e p" '("print s-exp" . eval-print-last-sexp))
Ligatures
The idea of using math symbols for a programming languages keywords is cute, but confusing when working with other people, and they are looking at my screen:
(set-ligatures! 'python-mode nil)
The rest of the ligature system in Doom is nice.
Task Runner
I've replaced my home-grown compilation list code with a more versatile Taskrunner project.
(setq ivy-taskrunner-notifications-on t
ivy-taskrunner-doit-bin-path "/usr/local/bin/doit")
Doom provides basic support, but we need more keybindings:
(map! :leader :prefix "p"
:desc "Project tasks" "Z" 'ivy-taskrunner
:desc "Reun last task" "z" 'ivy-taskrunner-rerun-last-command)
While my company is typically using Rakefile
and Makefile
in the top-level project, I want to have my personal tasks set per-project as well. For that, I thought about using doit, where I would just create a dodo.py
file that contains:
def hello():
"""This command greets you."""
return {
'actions': [ 'echo hello' ],
}
Display Configuration
Using the Doom Modeline to add notifications:
(setq doom-modeline-lsp t)
(setq doom-modeline-env-version t)
Languages
Simple to configure languages go here. More advanced stuff will go in their own files…eventually.
YAML/Ansible/Jinja
Doing a lot of YAML work, but this project needs a new maintainer.
(use-package yaml-mode
:mode "\\.ya?ml\\'")
Ansible uses Jinja, so we install the jinja2-mode:
(use-package jinja2-mode
:mode (rx ".j2" eol))
And, finally, we install Ansible as well:
(use-package ansible
:init
(setq ansible-vault-password-file "~/work/5/wpc5/deploy/playbooks/.vault-password")
:config
(add-hook 'yaml-mode-hook '(lambda () (ansible 1))))
And some special keybindings to encrypt/decrypt files:
(general-evil-define-key 'normal ansible-mode-map
:prefix "SPC m"
"x" '(:ignore t :which-key "decryption")
"x d" '("decrypt buffer" . ansible-decrypt-buffer)
"x e" '("encrypt buffer" . ansible-encrypt-buffer))
Emacs Lisp
Why yes, I do find I code a lot in Emacs…
(ha-auto-insert-file (rx ".el" eol) "emacs-lisp-mode.el")
However, most of my Emacs Lisp code is in literate org files.
Clever Parenthesis
The evil-cleverparens solves having me create keybindings to the smartparens project by updating the evil states with Lisp-specific bindings.
(use-package evil-cleverparens
:after smartparens-mode
:custom
evil-cleverparens-use-additional-bindings t
evil-cleverparens-use-additional-movement-keys t
evil-cleverparens-use-s-and-S nil ; using evil-sniper
:init
(require 'evil-cleverparens-text-objects)
:hook (emacs-lisp-mode . evil-cleverparens-mode))
I would like to have a list of what keybindings do what:
M-h
/M-l
move back/forward by functionsH
/L
move back/forward by s-expressionM-i
insert at the beginning of the formM-a
appends at the end of the formM-o
new form after the current sexpM-O
new form before the current sexpM-j
/M-k
drags thing at point and back and forth in the form>
slurp forward if at the end of form, at beginning, it barfs backwards<
slurp backward if at start of form, at the end, it barfs forwardsM-(
/M-)
wraps next/previous form in parens (braces and brackets work too)x
unwraps if the point is on the(
of an expression.D
deletes an entire s-expression, but this can depend on the position of the point.
The other advantage is moving around by s-expressions. This takes a little getting used to, for instance:
[
and]
move from paren to paren, essentially, from s-expression.H
andL
act similarly to the above.(
and)
move up to the parent s-expression
Other nifty keybindings that I need to commit to muscle memory include:
M-q |
sp-indent-defun |
M-J |
sp-join-sexp |
M-s |
sp-splice-sexp |
M-S |
sp-split-sexp |
M-t |
sp-transpose-sexp |
M-v |
sp-convolute-sexp |
M-r |
sp-raise-sexp |
Eval Current Expression
A feature I enjoyed from Spacemacs is the ability to evaluate the s-expression currently containing the point. Not sure how how they made it, but cleverparens can help:
(defun ha-eval-current-expression ()
"Evaluates the expression the point is currently 'in'.
It does this, by jumping to the end of the current
expression (using evil-cleverparens), and evaluating what it
finds at that point."
(interactive)
(save-excursion
(evil-cp-next-closing)
(evil-cp-forward-sexp)
(call-interactively 'eval-last-sexp)))
And we just need to bind it. The following is Doom-specific:
(general-evil-define-key 'normal prog-mode-map
:prefix "SPC m"
"e c" '("current" . ha-eval-current-expression))
Dim those Parenthesis
The paren-face project lowers the color level of parenthesis which I personally find better.
(use-package paren-face
:hook (emacs-lisp-mode . paren-face-mode))
I'm going to play with the parinfer package.
Lispy
Sacha had an interesting idea to generate a Hydra from a mode map:
key | function | column |
---|---|---|
< | lispy-barf | |
A | lispy-beginning-of-defun | |
j | lispy-down | |
Z | lispy-edebug-stop | |
B | lispy-ediff-regions | |
G | lispy-goto-local | |
h | lispy-left | |
N | lispy-narrow | |
y | lispy-occur | |
o | lispy-other-mode | |
J | lispy-outline-next | |
K | lispy-outline-prev | |
P | lispy-paste | |
l | lispy-right | |
I | lispy-shifttab | |
> | lispy-slurp | |
SPC | lispy-space | |
xB | lispy-store-region-and-buffer | |
u | lispy-undo | |
k | lispy-up | |
v | lispy-view | |
V | lispy-visit | |
W | lispy-widen | |
D | pop-tag-mark | |
x | see | |
L | unbound | |
U | unbound | |
X | unbound | |
Y | unbound | |
H | lispy-ace-symbol-replace | Edit |
c | lispy-clone | Edit |
C | lispy-convolute | Edit |
n | lispy-new-copy | Edit |
O | lispy-oneline | Edit |
r | lispy-raise | Edit |
R | lispy-raise-some | Edit |
\ | lispy-splice | Edit |
S | lispy-stringify | Edit |
i | lispy-tab | Edit |
xj | lispy-debug-step-in | Eval |
xe | lispy-edebug | Eval |
xT | lispy-ert | Eval |
e | lispy-eval | Eval |
E | lispy-eval-and-insert | Eval |
xr | lispy-eval-and-replace | Eval |
p | lispy-eval-other-window | Eval |
q | lispy-ace-paren | Move |
z | lispy-knight | Move |
s | lispy-move-down | Move |
w | lispy-move-up | Move |
t | lispy-teleport | Move |
Q | lispy-ace-char | Nav |
- | lispy-ace-subword | Nav |
a | lispy-ace-symbol | Nav |
b | lispy-back | Nav |
d | lispy-different | Nav |
f | lispy-flow | Nav |
F | lispy-follow | Nav |
g | lispy-goto | Nav |
xb | lispy-bind-variable | Refactor |
xf | lispy-flatten | Refactor |
xc | lispy-to-cond | Refactor |
xd | lispy-to-defun | Refactor |
xi | lispy-to-ifs | Refactor |
xl | lispy-to-lambda | Refactor |
xu | lispy-unbind-variable | Refactor |
M | lispy-multiline | Other |
xh | lispy-describe | Other |
m | lispy-mark-list | Other |
(defvar my-lispy-bindings bindings)
(defvar ha-hydra-lispy-bindings
(cl-loop for x in my-lispy-bindings
unless (string= "" (elt x 2))
collect
(list (car x)
(intern (elt x 1))
(when (string-match "lispy-\\(?:eval-\\)?\\(.+\\)"
(elt x 1))
(match-string 1 (elt x 1)))
:column
(elt x 2)))
"Collection of memorable Lispy functions")
(eval
`(defhydra
,(append '(("<f14>" nil :exit t)) ha-hydra-lispy-bindings )
))
(funcall defhydra
`(my/lispy-cheat-sheet (:hint nil :foreign-keys run)
))
(with-eval-after-load "lispy"
(define-key lispy-mode-map (kbd "<f14>") 'my/lispy-cheat-sheet/body))
Shell Scripts
While I don't like writing them, I can't get away from them.
While filename extensions work fine most of the time, I don't like to pre-pend .sh
to the few shell scripts I write, and instead, would like to associate shell-mode
with all files in a bin
directory:
(use-package sh-mode
:straight (:type built-in)
:mode (rx (or (seq ".sh" eol)
"/bin/"))
:config
(ha-auto-insert-file (rx (or (seq ".sh" eol)
"/bin/")) "sh-mode.sh")
:hook
(after-save . executable-make-buffer-file-executable-if-script-p))
Note: we make the script executable by default. See this essay for details, but it appears that the executable bit is only turned on if the script has a shebang at the top of the file.
Fish Shell
(use-package fish-mode
:mode (rx ".fish" eol)
:config
(ha-auto-insert-file (rx ".fish") "fish-mode.sh")
:hook
(fish-mode . (lambda () (add-hook 'before-save-hook 'fish_indent-before-save))))