586 lines
24 KiB
Org Mode
586 lines
24 KiB
Org Mode
#+TITLE: General Programming Configuration
|
||
#+AUTHOR: Howard X. Abrams
|
||
#+DATE: 2020-10-26
|
||
#+FILETAGS: :emacs:
|
||
|
||
A literate programming file for helping me program.
|
||
|
||
#+BEGIN_SRC emacs-lisp :exports none
|
||
;;; general-programming.el --- A literate programming file for helping me program. -*- lexical-binding: t; -*-
|
||
;;
|
||
;; Copyright (C) 2020 Howard X. Abrams
|
||
;;
|
||
;; Author: Howard X. Abrams <http://gitlab.com/howardabrams>
|
||
;; Maintainer: Howard X. Abrams
|
||
;; Created: October 26, 2020
|
||
;;
|
||
;; This file is not part of GNU Emacs.
|
||
;;
|
||
;; *NB:* Do not edit this file. Instead, edit the original literate file at:
|
||
;; ~/other/hamacs/general-programming.org
|
||
;; And tangle the file to recreate this one.
|
||
;;
|
||
;;; Code:
|
||
#+END_SRC
|
||
|
||
* Introduction
|
||
|
||
Seems that all programming interfaces and workflows behave similarly. However...
|
||
|
||
One other helper routine is a =general= macro for org-mode files:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(general-create-definer ha-prog-leader
|
||
:states '(normal visual motion)
|
||
:keymaps 'prog-mode-map
|
||
:prefix "SPC m"
|
||
:global-prefix "<f17>"
|
||
:non-normal-prefix "S-SPC")
|
||
#+END_SRC
|
||
* General
|
||
The following work for all programming languages.
|
||
** direnv
|
||
Farm off commands into /virtual environments/:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package direnv
|
||
:init
|
||
(setq direnv--executable "/usr/local/bin/direnv"
|
||
direnv-always-show-summary t
|
||
direnv-show-paths-in-summary t)
|
||
:config
|
||
(direnv-mode))
|
||
#+END_SRC
|
||
** Spell Checking Comments
|
||
The [[https://www.emacswiki.org/emacs/FlySpell#h5o-2][flyspell-prog-mode]] only checks for misspellings in comments. Useful!
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package flyspell
|
||
:hook (prog-mode . flyspell-prog-mode))
|
||
#+END_SRC
|
||
** Flymake
|
||
Grab the latest version of [[https://www.emacswiki.org/emacs/FlyMake][Flymake]] in order to integrate with LSP. While we are at it, let’s add some keybindings.
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(use-package flymake
|
||
:config
|
||
(ha-prog-leader
|
||
">" '("next problem" . flymake-goto-next-error)
|
||
"<" '("previous problem" . flymake-goto-prev-error)
|
||
"p" '(:ignore t :which-key "problems")
|
||
"p s" '("start checking" . flymake-start)
|
||
"p t" '("toggle flymake" . flymake-mode)
|
||
"p p" '("show problem" . flymake-show-diagnostic)
|
||
"p P" '("list all problems" . flymake-show-buffer-diagnostics)
|
||
"p l" '("show log buffer" . flymake-switch-to-log-buffer)
|
||
"p b" '("show log buffer" . flymake-running-backends)))
|
||
#+END_SRC
|
||
The question is why not use the newer [[https://www.flycheck.org/][flycheck]]? Speed used to be the advantage, however, I’m now pushing this stuff to LSP, so speed is less of an issue. However, what about when I am not using LSP?
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package flycheck
|
||
:bind (:map flycheck-error-list-mode-map
|
||
("C-n" . 'flycheck-error-list-next-error)
|
||
("C-p" . 'flycheck-error-list-previous-error)
|
||
("j" . 'flycheck-error-list-next-error)
|
||
("k" . 'flycheck-error-list-previous-error))
|
||
:config
|
||
(flymake-mode -1)
|
||
(global-flycheck-mode)
|
||
(ha-leader "t c" 'flycheck-mode)
|
||
|
||
(ha-prog-leader
|
||
">" '("next problem" . flycheck-next-error)
|
||
"<" '("previous problem" . flycheck-previous-error)
|
||
|
||
"p" '(:ignore t :which-key "problems")
|
||
"p b" '("error buffer" . flycheck-buffer)
|
||
"p c" '("clear" . flycheck-clear)
|
||
"p n" '("next" . flycheck-next-error)
|
||
"p p" '("previous" . flycheck-previous-error)
|
||
"p l" '("list all" . flycheck-list-errors)
|
||
"p y" '("copy errors" . flycheck-copy-errors-as-kill)
|
||
"p s" '("select checker" . flycheck-select-checker)
|
||
"p ?" '("describe checker" . flycheck-describe-checker)
|
||
"p h" '("display error" . flycheck-display-error-at-point)
|
||
"p e" '("explain error" . flycheck-explain-error-at-point)
|
||
"p H" '("help" . display-local-help)
|
||
"p i" '("manual" . flycheck-manual)
|
||
"p V" '("version" . flycheck-version)
|
||
"p v" '("verify-setup" . flycheck-verify-setup)
|
||
"p x" '("disable-checker" . flycheck-disable-checker)
|
||
"p t" '("toggle flycheck" . flycheck-mode)))
|
||
#+END_SRC
|
||
** Documentation
|
||
I’ve used the [[http://kapeli.com/][Dash]] API Documentation browser (an external application) with Emacs, however, this is only available for Mac.
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(use-package dash-at-point
|
||
:commands (dash-at-point)
|
||
:config
|
||
(define-key evil-normal-state-map (kbd "g D") 'dash-at-point))
|
||
#+END_SRC
|
||
However, I’m interested in using [[https://devdocs.io/][devdocs]] instead, which is similar, but keeps it all /inside/ Emacs (and works on my Linux system). There are seems to be two competing Emacs projects for this.
|
||
|
||
The Emacs [[https://github.com/astoff/devdocs.el][devdocs]] project is active, and seems to work well. Its advantage is a special mode for moving around the documentation.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package devdocs
|
||
:after evil
|
||
:config
|
||
(define-key evil-normal-state-map (kbd "g D") 'devdocs-lookup)
|
||
|
||
(ha-prog-leader
|
||
"d" '(:ignore t :which-key "docs")
|
||
"d d" '("open" . devdocs-lookup)
|
||
"d p" '("peruse" . devdocs-peruse)
|
||
"d i" '("install" . devdocs-install)
|
||
"d u" '("update" . devdocs-update-all)
|
||
"d x" '("uninstall" . devdocs-delete)
|
||
"d s" '("search" . devdocs-search)))
|
||
#+END_SRC
|
||
|
||
The [[https://github.com/blahgeek/emacs-devdocs-browser][devdocs-browser]] project acts similar, but with slightly different command names. Its advantage is that it allows for downloading docs and having it available offline, in fact, you can’t search for a function, until you download its pack. This is slightly faster because of this.
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(use-package devdocs-browser
|
||
:config
|
||
(define-key evil-normal-state-map (kbd "g D") 'devdocs-browser-open)
|
||
|
||
(ha-prog-leader
|
||
"d" '(:ignore t :which-key "docs")
|
||
"d d" '("open" . devdocs-browser-open)
|
||
"d D" '("open in" . devdocs-browser-open-in)
|
||
"d l" '("list" . devdocs-browser-list-docs)
|
||
"d u" '("update" . devdocs-browser-update-docs)
|
||
"d i" '("install" . devdocs-browser-install-doc)
|
||
"d x" '("uninstall" . devdocs-browser-uninstall-doc)
|
||
"d U" '("upgrade" . devdocs-browser-upgrade-doc)
|
||
"d o" '("download" . devdocs-browser-download-offline-data)
|
||
"d O" '("remove download" . devdocs-browser-remove-offline-data)))
|
||
#+END_SRC
|
||
** Language Server Protocol (LSP) Integration
|
||
The [[https://microsoft.github.io/language-server-protocol/][LSP]] is a way to connect /editors/ (like Emacs) to /languages/ (like Lisp) ... wait, no, it was originally designed for VS Code and probably Python, but we now abstract away [[https://github.com/davidhalter/jedi][Jedi]] and the [[http://tkf.github.io/emacs-jedi/latest/][Emacs integration to Jedi]] (and duplicate everything for Ruby, and Clojure, and...).
|
||
|
||
Instead, we install [[https://emacs-lsp.github.io/lsp-mode/][LSP Mode]] (and friends), which simplifies my configuration:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package lsp-mode
|
||
:commands lsp
|
||
:init
|
||
;; Let's make lsp-doctor happy with these settings:
|
||
(setq gc-cons-threshold (* 100 1024 1024)
|
||
read-process-output-max (* 1024 1024)
|
||
company-idle-delay 0.0 ; Are thing fast enough to do this?
|
||
lsp-keymap-prefix "s-m")
|
||
:hook ((lsp-mode . lsp-enable-which-key-integration)))
|
||
#+END_SRC
|
||
I will want to start adding commands under my =SPC m= mode-specific key sequence leader, but in the meantime, all LSP-related keybindings are available under ~⌘-m~. See [[https://emacs-lsp.github.io/lsp-mode/page/keybindings/][this page]] for the default keybindings.
|
||
*** UI
|
||
The [[https://github.com/emacs-lsp/lsp-ui][lsp-ui]] project offers much of the display and interface to LSP:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package lsp-ui
|
||
:commands lsp-ui-mode
|
||
:config
|
||
(setq lsp-ui-sideline-ignore-duplicate t
|
||
lsp-ui-sideline-show-hover t
|
||
lsp-ui-sideline-show-diagnostics t)
|
||
:hook (lsp-mode . lsp-ui-mode))
|
||
#+END_SRC
|
||
*** Company Completion
|
||
The [[https://github.com/tigersoldier/company-lsp][company-lsp]] offers a [[http://company-mode.github.io/][company]] completion backend for [[https://github.com/emacs-lsp/lsp-mode][lsp-mode]]:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package company-lsp
|
||
:config
|
||
(push 'company-lsp company-backends))
|
||
#+END_SRC
|
||
To options that might be interesting:
|
||
- =company-lsp-async=: When set to non-nil, fetch completion candidates asynchronously.
|
||
- =company-lsp-enable-snippet=: Set it to non-nil if you want to enable snippet expansion on completion. Set it to nil to disable this feature.
|
||
|
||
*** iMenu
|
||
The [[https://github.com/emacs-lsp/lsp-ui/blob/master/lsp-ui-imenu.el][lsp-imenu]] project offers a =lsp-ui-imenu= function for jumping to functions:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package lsp-ui-imenu
|
||
:straight nil
|
||
:after lsp-ui
|
||
:config
|
||
(ha-prog-leader
|
||
"g" '(:ignore t :which-key "goto")
|
||
"g m" '("imenu" . lsp-ui-imenu))
|
||
(add-hook 'lsp-after-open-hook 'lsp-enable-imenu))
|
||
#+END_SRC
|
||
*** Treemacs
|
||
The [[https://github.com/emacs-lsp/lsp-treemacs][lsp-treemacs]] offers a project-specific structure oriented to the code:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package lsp-treemacs
|
||
:config
|
||
(ha-prog-leader
|
||
"0" '("treemacs" . lsp-treemacs-symbols)))
|
||
#+END_SRC
|
||
|
||
*** Debugging
|
||
Do we want to use a debugger?
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(use-package dap-mode
|
||
:init
|
||
(require 'dap-python))
|
||
#+END_SRC
|
||
** Function Call Notifications
|
||
As I've mentioned [[http://www.howardism.org/Technical/Emacs/beep-for-emacs.html][on my website]], I've created a [[file:~/website/Technical/Emacs/beep-for-emacs.org][beep function]] that notifies when long running processes complete.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package alert
|
||
:init
|
||
(setq alert-default-style
|
||
(if (ha-running-on-macos?)
|
||
'osx-notifier
|
||
'libnotify)))
|
||
|
||
(use-package beep
|
||
:straight nil ; Already in the load-path
|
||
:hook (after-init . (lambda () (beep--when-finished "Emacs has started")))
|
||
:config
|
||
(dolist (func '(org-publish
|
||
org-publish-all
|
||
org-publish-project
|
||
compile
|
||
shell-command))
|
||
(advice-add func :around #'beep-when-runs-too-long)))
|
||
#+END_SRC
|
||
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, [[https://github.com/victorhge/iedit][iedit]] is often sufficient.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(ha-leader "s e" '("iedit" . iedit-mode))
|
||
#+END_SRC
|
||
** Commenting
|
||
I like =comment-dwim= (~M-;~), and I like =comment-box=, but I have an odd personal style that I like to codify:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(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)))
|
||
#+END_SRC
|
||
And a keybinding:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(ha-prog-leader "c" '("comment line" . ha-comment-line))
|
||
#+END_SRC
|
||
** Evaluation
|
||
Typical keybindings for all programming modes:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(ha-prog-leader
|
||
"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))
|
||
#+END_SRC
|
||
** 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:
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(set-ligatures! 'python-mode nil)
|
||
#+END_SRC
|
||
|
||
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 [[https://github.com/emacs-taskrunner/emacs-taskrunner][Taskrunner project]].
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(setq ivy-taskrunner-notifications-on t
|
||
ivy-taskrunner-doit-bin-path "/usr/local/bin/doit")
|
||
#+END_SRC
|
||
|
||
Doom provides basic support, but we need more keybindings:
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle no
|
||
(map! :leader :prefix "p"
|
||
:desc "Project tasks" "Z" 'ivy-taskrunner
|
||
:desc "Reun last task" "z" 'ivy-taskrunner-rerun-last-command)
|
||
#+END_SRC
|
||
|
||
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 [[https://pydoit.org/][doit]], where I would just create a =dodo.py= file that contains:
|
||
|
||
#+BEGIN_SRC python :tangle no
|
||
def hello():
|
||
"""This command greets you."""
|
||
return {
|
||
'actions': [ 'echo hello' ],
|
||
}
|
||
#+END_SRC
|
||
|
||
** Display Configuration
|
||
Using the [[https://github.com/seagle0128/doom-modeline][Doom Modeline]] to add notifications:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(setq doom-modeline-lsp t)
|
||
(setq doom-modeline-env-version t)
|
||
#+END_SRC
|
||
* Languages
|
||
Simple to configure languages go here. More advanced stuff will go in their own files...eventually.
|
||
** YAML and Jinja
|
||
Doing a lot of [[https://github.com/yoshiki/yaml-mode][YAML work]], but this project needs a new maintainer.
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package yaml-mode
|
||
:mode "\\.ya?ml\\'")
|
||
#+END_SRC
|
||
|
||
Ansible uses Jinja, so we install the [[https://github.com/paradoxxxzero/jinja2-mode][jinja2-mode]]:
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package jinja2-mode
|
||
:mode (rx ".j2" eol))
|
||
#+END_SRC
|
||
|
||
** Emacs Lisp
|
||
|
||
Why yes, I do find I code a lot in Emacs...
|
||
#+BEGIN_SRC emacs-lisp
|
||
(ha-auto-insert-file (rx ".el" eol) "emacs-lisp-mode.el")
|
||
#+END_SRC
|
||
However, most of my Emacs Lisp code is in literate org files.
|
||
|
||
*** Clever Parenthesis
|
||
|
||
The [[https://github.com/luxbock/evil-cleverparens][evil-cleverparens]] solves having me create keybindings to the [[https://github.com/Fuco1/smartparens][smartparens]] project by updating the evil states with Lisp-specific bindings.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(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 (prog-mode . evil-cleverparens-mode)) ;; All the languages!
|
||
;; Otherwise: (emacs-lisp-mode . evil-cleverparens-mode)
|
||
#+END_SRC
|
||
|
||
I would like to have a list of what keybindings do what:
|
||
- ~M-h~ / ~M-l~ move back/forward by functions
|
||
- ~H~ / ~L~ move back/forward by s-expression
|
||
- ~M-i~ insert at the beginning of the form
|
||
- ~M-a~ appends at the end of the form
|
||
- ~M-o~ new form after the current sexp
|
||
- ~M-O~ new form /before/ the current sexp
|
||
- ~M-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 forwards
|
||
- ~M-(~ / ~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~ and ~L~ 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:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(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)))
|
||
#+END_SRC
|
||
|
||
And we just need to bind it. The following is Doom-specific:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(ha-prog-leader
|
||
"e c" '("current" . ha-eval-current-expression))
|
||
#+END_SRC
|
||
*** Dim those Parenthesis
|
||
The [[https://github.com/tarsius/paren-face][paren-face]] project lowers the color level of parenthesis which I personally find better.
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(use-package paren-face
|
||
:hook (emacs-lisp-mode . paren-face-mode))
|
||
#+END_SRC
|
||
I'm going to play with the [[https://github.com/DogLooksGood/parinfer-mode][parinfer]] package.
|
||
*** Lispy
|
||
Sacha had an [[https://sachachua.com/blog/2021/04/emacs-making-a-hydra-cheatsheet-for-lispy/][interesting idea]] to /generate/ a Hydra from a mode map:
|
||
|
||
#+NAME: bindings
|
||
| 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 |
|
||
|
||
|
||
#+BEGIN_SRC emacs-lisp :var bindings=bindings :colnames yes :tangle no
|
||
(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))
|
||
#+END_SRC
|
||
|
||
** 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:
|
||
|
||
#+BEGIN_SRC emacs-lisp
|
||
(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))
|
||
#+END_SRC
|
||
*Note:* we make the script /executable/ by default. See [[https://emacsredux.com/blog/2021/09/29/make-script-files-executable-automatically/][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
|
||
#+BEGIN_SRC emacs-lisp
|
||
(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))))
|
||
#+END_SRC
|
||
* Technical Artifacts :noexport:
|
||
Provide a name in order to =require= this code.
|
||
|
||
#+BEGIN_SRC emacs-lisp :exports none
|
||
(provide 'ha-programming)
|
||
;;; ha-programming.el ends here
|
||
#+END_SRC
|
||
|
||
Before you can build this on a new system, make sure that you put the cursor over any of these properties, and hit: ~C-c C-c~
|
||
|
||
#+DESCRIPTION: A literate programming file for helping me program.
|
||
|
||
#+PROPERTY: header-args:sh :tangle no
|
||
#+PROPERTY: header-args:emacs-lisp yes
|
||
#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes
|
||
|
||
#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil
|
||
#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil
|
||
#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js
|