diff --git a/README.org b/README.org index 98aa48a..d0eb563 100644 --- a/README.org +++ b/README.org @@ -1,13 +1,12 @@ #+TITLE: My Emacs Configuration #+AUTHOR: Howard X. Abrams #+DATE: 2021-11-01 November -#+TAGS: emacs My Emacs configuration, that I'm cheekily calling /hamacs/ is a literate programming model heavily inspired by my recent journey into [[https://www.youtube.com/watch?v=LKegZI9vWUU][Henrik Lissner's]] [[https://github.com/hlissner/doom-emacs][Doom Emacs]] and [[https://www.spacemacs.org/][Spacemacs]]. I used both extensively, but decided that I would /roll my own/ as Emacs people tend to be /control freaks/ (at least a little bit). -The other advantage to rolling yer own is that you are more likely to /use/ what you add, leading to less bloat, and a more fun experience. +The other advantage to rolling yer own is that you may /use/ what you add, leading to less bloat, and a more fun experience. -Why yes, feel free to steal whatever you find interesting, as sharing is what makes our community great. Notice that functions and features that I have written begin with ~ha-~, however, everything else is either /stock Emacs/ or a /package/ that I download using [[https://github.com/raxod502/straight.el][straight]] (see [[file:bootstrap.org][bootstrap]] for how) and configured with [[https://github.com/jwiegley/use-package][use-package]] (see either [[https://ianyepan.github.io/posts/setting-up-use-package/][this introduction]] or [[https://www.emacswiki.org/emacs/UsePackage][this wiki page]] for details)... meaning that most blocks of code should /just work/ on its own. +Why yes, feel free to steal whatever you find interesting, as sharing is what makes our community great. Notice that functions and features that I have written begin with ~ha-~, but everything else is either /stock Emacs/ or a /package/ that I download using [[https://github.com/raxod502/straight.el][straight]] (see [[file:bootstrap.org][bootstrap]] for how) and configured with [[https://github.com/jwiegley/use-package][use-package]] (see either [[https://ianyepan.github.io/posts/setting-up-use-package/][this introduction]] or [[https://www.emacswiki.org/emacs/UsePackage][this wiki page]] for details)… meaning that most blocks of code should work on its own. Hit me up with questions, =@howardabrams=. If you want to try this out, after installing Emacs, and cloning this repo, run: #+BEGIN_SRC sh ./initialize @@ -17,7 +16,7 @@ This creates [[file:~/.emacs.d/init.el][~/.emacs.d/init.el]] that starts the pro - [[file:bootstrap.org][bootstrap]] :: configures =straight= and loads basic libraries the rest of the code depends on. It then loads the following files in order: - [[file:ha-config.org][config]] :: contains /most/ of my configuration, setting up my sequence key menus, evil, etc. - [[file:ha-display.org][display]] :: sets up the visual aspects of an Emacs GUI, including themes, fonts and the dashboard. - - [[file:ha-org.org][org]] :: configures the basics for org-mode formatted files. Specific features, however, come from their own files, however. + - [[file:ha-org.org][org]] :: configures the basics for org-mode formatted files. Specific features come from their own files. - [[file:ha-org-word-processor.org][org-word-processor]] :: attempts to make Org files /visually/ look like a word processor, including turning off the colors for headers, and instead increasing their size. - [[file:ha-org-clipboard.org][org-clipboard]] :: automatically converting HTML from a clipboard into Org-formatted content. - [[file:ha-org-journaling.org][org-journaling]] :: for writing journal entries and tasks. @@ -35,6 +34,5 @@ This creates [[file:~/.emacs.d/init.el][~/.emacs.d/init.el]] that starts the pro - [[file:ha-programming-elisp.org][programming-elisp]] :: additions to Emacs Lisp programming. - [[file:ha-programming-python.org][programming-python]] :: configuration for working with Python and LSP. - [[file:ha-programming-scheme.org][programming-scheme]] :: configuration for Racket. - - [[file:ha-aux-apps.org][aux-apps]] :: additional application configuration. *Note:* Other functions and files come from essays written on [[http://www.howardism.org][my blog]]. To help with this, see [[file:support/final-initialize.el][support/final-initialize.el]] file. diff --git a/bootstrap.org b/bootstrap.org index f1b7453..119e664 100644 --- a/bootstrap.org +++ b/bootstrap.org @@ -1,15 +1,14 @@ #+TITLE: My Emacs Bootstrap #+AUTHOR: Howard X. Abrams #+DATE: 2021-10-08 -#+FILETAGS: :emacs: A literate programming file for bootstraping my Emacs Configuration. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; bootstrap.el --- file for bootstraping my Emacs Configuration ;; ;; © 2021-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,13 +22,13 @@ A literate programming file for bootstraping my Emacs Configuration. ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC +#+end_src * Introduction This file contains all the variable definitions and library loading for the other files in my project. ** Straight Package Installer -I'm going to be installing everything using the [[https://github.com/raxod502/straight.el#getting-started][straight.el]] for package installation and management. However, before I could tangle these org files, I needed to have =straight= grab the latest =org=, so the following initialization code is actually in [[file:initialize][initialize]], but the good stuff is: +I'm going to be installing everything using the [[https://github.com/raxod502/straight.el#getting-started][straight.el]] for package installation and management. Before I can /tangle/ these files, I need =straight= to grab the latest =org=, so the following initialization code is actually in [[file:initialize][initialize]], but if you are reading this online, configuring =straight= amounts to the following: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (defvar bootstrap-version) (let ((bootstrap-file @@ -43,24 +42,24 @@ I'm going to be installing everything using the [[https://github.com/raxod502/st (goto-char (point-max)) (eval-print-last-sexp))) (load bootstrap-file nil 'nomessage)) -#+END_SRC -Let's get the Straight project working with =use-package=: +#+end_src -#+BEGIN_SRC emacs-lisp :tangle no +To get the Straight project working with =use-package=: +#+begin_src emacs-lisp :tangle no (straight-use-package 'use-package) -#+END_SRC +#+end_src -While that enables the =:straight t= extension to =use-package=, let's just have that be the default: -#+BEGIN_SRC emacs-lisp :tangle no +While the above code enables the =:straight t= extension to =use-package=, let's have that as the default: +#+begin_src emacs-lisp :tangle no (use-package straight :custom (straight-use-package-by-default t straight-default-vc 'git)) -#+END_SRC +#+end_src See the details in [[https://dev.to/jkreeftmeijer/emacs-package-management-with-straight-el-and-use-package-3oc8][this essay]]. ** OS Path and Native Compilation Helper functions to allow code for specific operating systems: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-running-on-macos? () "Return non-nil if running on Mac OS systems." (equal system-type 'darwin)) @@ -68,17 +67,16 @@ Helper functions to allow code for specific operating systems: (defun ha-running-on-linux? () "Return non-nil if running on Linux systems." (equal system-type 'gnu/linux)) -#+END_SRC +#+end_src -With the way I start Emacs, I may not have the PATH I /actually/ use (from the shell) available, so we'll force it (code taken [[https://www.emacswiki.org/emacs/ExecPath][from here]]): +With the way I start Emacs, I may not have the =PATH= I /actually/ use (from the shell) available, so we'll force it (code taken [[https://www.emacswiki.org/emacs/ExecPath][from here]]): -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun set-exec-path-from-shell () "Set up Emacs' `exec-path' and PATH environment variable to match that used by the user's shell. - This is particularly useful under Mac OS X and macOS, where GUI - apps are not started from a shell." + The MacOS, where GUI apps are not started from a shell, requires this." (interactive) (let* ((path-from-shell (shell-command-to-string "echo $PATH")) (trimmed-path (replace-regexp-in-string (rx (zero-or-more space) eol) @@ -90,35 +88,35 @@ With the way I start Emacs, I may not have the PATH I /actually/ use (from the s (message "PATH=%s" path-from-shell) (setenv "PATH" env-path) (setq exec-path (split-string trimmed-path separator)))) -#+END_SRC +#+end_src Clear up a Mac-specific issue that sometimes arises since I'm switching to [[http://akrl.sdf.org/gccemacs.html][native compilation project]], as the =Emacs.app= that I use doesn't have its =bin= directory, e.g. =Emacs.app/Contents/MacOS/bin=: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (when (ha-running-on-macos?) (add-to-list 'exec-path "/usr/local/bin") (add-to-list 'exec-path (concat invocation-directory "bin") t)) -#+END_SRC +#+end_src Getting tired off all the packages that I load spewing a bunch of warnings that I can't do anything about: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (when (and (fboundp 'native-comp-available-p) (native-comp-available-p)) (setq native-comp-async-report-warnings-errors nil native-comp-deferred-compilation t)) -#+END_SRC +#+end_src ** GNU Pretty Good Privacy On Linux, GPG is pretty straight-forward, but on the Mac, I often have troubles doing: -#+BEGIN_SRC sh +#+begin_src sh brew install gpg -#+END_SRC +#+end_src Next, on every reboot, start the agent: -#+BEGIN_SRC sh +#+begin_src sh /usr/local/Cellar/gnupg/2.3.6/bin/gpg-agent --daemon -#+END_SRC -The only issue is that =brew link gpg= doesn’t always work, so maybe a helper function to find the executable: +#+end_src -#+BEGIN_SRC emacs-lisp +Since =brew link gpg= doesn’t always work, this helper function may find the executable: +#+begin_src emacs-lisp (defun executable (path) "Return PATH if it is executable, see `file-executable-p'." (let ((epath (first (file-expand-wildcards path)))) @@ -131,56 +129,56 @@ The only issue is that =brew link gpg= doesn’t always work, so maybe a helper (executable "/usr/local/opt/gpg") (executable "/usr/bin/pgp"))) :config (epa-file-enable)) -#+END_SRC +#+end_src ** Basic Libraries The following packages come with Emacs, but seems like they still need loading: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package cl-lib :straight (:type built-in) :init (defun first (elt) (car elt)) :commands (first)) (require 'subr-x) -#+END_SRC +#+end_src Ugh. Why am I getting a missing =first= function error? I define a simple implementation, that the CL library will overwrite ... at some point. -While most libraries will take care of their dependencies, I want to install /my dependent libraries/. Especially, [[https://github.com/magnars/.emacs.d/][Magnar Sveen]]'s Clojure-inspired [[https://github.com/magnars/dash.el][dash.el]] project: -#+BEGIN_SRC emacs-lisp +While most libraries will take care of their dependencies, I want to install /my dependent libraries/, e.g, [[https://github.com/magnars/.emacs.d/][Magnar Sveen]]'s Clojure-inspired [[https://github.com/magnars/dash.el][dash.el]] project: +#+begin_src emacs-lisp (use-package dash) -#+END_SRC +#+end_src Sure this package is essentially syntactic sugar, and to help /share/ my configuration, I attempt to use =thread-last= instead of =->>=, but, I still like it. The [[https://github.com/magnars/s.el][s.el]] project is a simpler string manipulation library that I (and other projects) use: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package s) -#+END_SRC +#+end_src Manipulate file paths with the [[https://github.com/rejeep/f.el][f.el]] project: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package f) -#+END_SRC +#+end_src ** My Code Location -Much of my more complicated code comes from my website essays and other projects. The destination, however, shows up here: -#+BEGIN_SRC emacs-lisp +Much of my more complicated code comes from my website essays and other projects. The destination shows up here: +#+begin_src emacs-lisp (add-to-list 'load-path (f-expand "~/.emacs.d/elisp")) -#+END_SRC +#+end_src Hopefully, this will tie me over while I transition. ** Emacs Server Control -Sure the Emacs application will almost always have the =server-start= going, however, I need to control it just a bit (because I often have two instances running on some of my machines). What /defines/ the Emacs instance for work changes ... often: +Sure the Emacs application will almost always have the =server-start= going, but I need to control it (because I often have two instances running on some of my machines). What /defines/ the Emacs instance for work changes ... often: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-emacs-for-work? () "Return non-nil when the Emacs application's location matches as one for work. - Currently, this is the `emacs-plus@28' app that I have built with + This is the `emacs-plus@28' app that I have built with the native-comp model, but I reserve the right to change this." (and (f-dir? "~/work") ;; (string-match "emacs-plus@28" exec-directory) (not (string-match "Emacs.app" exec-directory)))) -#+END_SRC +#+end_src And now start the server with an appropriate tag name: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (if (not (ha-emacs-for-work?)) (setq server-name "personal") (setq server-name "work") @@ -188,15 +186,16 @@ And now start the server with an appropriate tag name: (set-exec-path-from-shell))) (server-start) -#+END_SRC +#+end_src * Load the Rest -The following loads the rest of my org-mode literate files. I add them as they are /ready/, but eventually, I'll trim this up into a nicer pattern. -#+BEGIN_SRC emacs-lisp - (defvar ha-hamacs-files (flatten-list `("ha-private.org" - "ha-config.org" +The following loads the rest of my org-mode literate files. I add new filesas they are /ready/: +#+begin_src emacs-lisp + (defvar ha-hamacs-files (flatten-list + `("ha-private.org" + "ha-config.org" ,(when (display-graphic-p) "ha-display.org") - "ha-org.org" + "ha-org.org" ,(when (display-graphic-p) "ha-org-word-processor.org") "ha-org-clipboard.org" @@ -217,49 +216,48 @@ The following loads the rest of my org-mode literate files. I add them as they a "ha-aux-apps.org" "ha-feed-reader.org")))) "List of org files that complete the hamacs project.") -#+END_SRC +#+end_src We can test/debug/reload any individual file, via: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-hamacs-load (file) "Load or reload an org-mode FILE containing literate Emacs configuration code." (interactive (list (completing-read "Org file: " ha-hamacs-files))) (let ((full-file (f-join hamacs-source-dir file))) (when (f-exists? full-file) (org-babel-load-file full-file)))) -#+END_SRC +#+end_src And we can now load everything: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-hamacs-reload-all () "Reload our entire ecosystem of configuration files." (interactive) (dolist (file ha-hamacs-files) (unless (equal file "bootstrap.org") (ha-hamacs-load file)))) -#+END_SRC +#+end_src And do it: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-hamacs-reload-all) -#+END_SRC +#+end_src -Once we have loaded /my world/, let’s add every other Org file in the project to the list, so that I can easily bring more stuff. -#+BEGIN_SRC emacs-lisp +Once we have loaded /my world/, let’s add every other Org file in the project to the list, so that I can load newly created files that I don’t want to commit: +#+begin_src emacs-lisp (setq ha-hamacs-files (->> (rx ".org" string-end) (directory-files "~/other/hamacs" nil) (append ha-hamacs-files) (--filter (not (string-match (rx "README") it))) (-uniq))) -#+END_SRC +#+end_src * Technical Artifacts :noexport: -Let's provide a name so that the file can be required: - -#+BEGIN_SRC emacs-lisp :exports none -(provide 'bootstrap) -;;; bootstrap.el ends here -#+END_SRC +Let's provide a name so we can =require= this file: +#+begin_src emacs-lisp :exports none + (provide 'bootstrap) + ;;; bootstrap.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~ diff --git a/ha-agendas.org b/ha-agendas.org index 4754524..7bdcfaf 100644 --- a/ha-agendas.org +++ b/ha-agendas.org @@ -1,15 +1,14 @@ #+TITLE: Org Agenda Configuration #+AUTHOR: Howard X. Abrams #+DATE: 2020-09-18 -#+FILETAGS: :emacs: A literate programming configuration for fancy agenda and todo lists. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-agendas --- Configuration for fancy agenda and todo lists. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,20 +22,20 @@ A literate programming configuration for fancy agenda and todo lists. ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC +#+end_src * Introduction All the code we describe in this file needs loading /after/ org. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (with-eval-after-load "org" (add-to-list 'org-modules 'org-protocol)) -#+END_SRC +#+end_src * Grouping Typical agendas have an /order/ to them, but the [[https://github.com/alphapapa/org-super-agenda][org-super-agenda project]] allows you to get specific as well as group them under headings. Unless you specify otherwise, this is the grouping we'll use: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package org-super-agenda :after org :init @@ -62,12 +61,12 @@ Unless you specify otherwise, this is the grouping we'll use: :todo "TODO" :scheduled future :order 3)))) -#+END_SRC +#+end_src The task matches a group based on the /code order/, but the =:order= tag allows me to display them in a different order. The following super agenda is just for /today/ and should be smaller: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (setq ha-org-super-agenda-today '((:name "Finished" :todo ("DONE" "CANCELED") @@ -80,13 +79,13 @@ The following super agenda is just for /today/ and should be smaller: :scheduled past :date today :order 0))) -#+END_SRC +#+end_src * Query Views The [[https://github.com/alphapapa/org-ql][org-ql project]] gives us a /query language/ of sorts (based on s-expressions). By putting all queries under =org-ql-views=, we can then call ~M-x query~ and select the view to display: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package org-ql :after org :config @@ -137,27 +136,27 @@ By putting all queries under =org-ql-views=, we can then call ~M-x query~ and se :title "Today" :super-groups 'ha-org-super-agenda-today :sort '(priority)))))) -#+END_SRC +#+end_src * Agenda Interface We can create a function to start this: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-todays-agenda () "Display an agenda for today, including tasks and scheduled entries." (interactive) (org-ql-view "Overview: Today")) -#+END_SRC +#+end_src And of course, a keybinding: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "a a" '("my agenda" . ha-todays-agenda)) -#+END_SRC +#+end_src * Technical Artifacts :noexport: Let's provide a name so that the file can be required: -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none (provide 'ha-agendas) ;;; ha-agendas.el ends here -#+END_SRC +#+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~ diff --git a/ha-aux-apps.org b/ha-aux-apps.org index 2c76a33..bf3d030 100644 --- a/ha-aux-apps.org +++ b/ha-aux-apps.org @@ -1,15 +1,14 @@ #+TITLE: Auxillary and Optional Applications #+AUTHOR: Howard X. Abrams #+DATE: 2021-11-18 -#+FILETAGS: :emacs: A literate programming file for helper apps in Emacs. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-aux-apps --- Configuring helper apps in Emacs. -*- lexical-binding: t; -*- ;; ;; © 2021-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,13 +22,13 @@ A literate programming file for helper apps in Emacs. ;; And tangle the file to recreate this one. ;; ;;; Code: - #+END_SRC + #+end_src * Introduction -The following applications are not really needed. I alternate between trying to /stay in Emacs/ taking advantage of the consistent interface, and simply using a stand-alone app on my Workday computer. +The following applications are not needed. I alternate between trying to /stay in Emacs/ taking advantage of the consistent interface, and using a stand-alone app on my Workday computer. * Twitter The venerable [[https://github.com/hayamiz/twittering-mode/tree/master][twittering-mode]] allows me to follow all the twits. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package twittering-mode :init (setq twittering-use-master-password t @@ -37,11 +36,11 @@ The venerable [[https://github.com/hayamiz/twittering-mode/tree/master][twitteri :config (defalias 'epa--decode-coding-string 'decode-coding-string) (ha-leader "a t" '("twitter" . twit))) -#+END_SRC +#+end_src * Telega -I'm thinking the [[https://zevlg.github.io/telega.el/][Telega package]] would be better than Bitlbee for Telegram communication. Seems to have a bug on the Melpa version, so I'm keeping this to the =HEAD=, but only if I've cloned it. +I'm thinking the [[https://zevlg.github.io/telega.el/][Telega package]] would be better than Bitlbee for Telegram communication. Seems to have a bug on the Melpa version, so I'm keeping this to the =HEAD=, if I've cloned it. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (when (file-exists-p "~/other/telega.el") (use-package telega :straight (:local-repo "~/other/telega.el") @@ -50,22 +49,22 @@ I'm thinking the [[https://zevlg.github.io/telega.el/][Telega package]] would be (setq telega-use-images nil) :config (ha-leader "a T" 'telega))) -#+END_SRC +#+end_src For some reason, you need [[https://github.com/Fanael/rainbow-identifiers][rainbow-identifiers]] to work, oh, I guess the docs state this. * RPG DM Been working on a project for getting Emacs helping as a /Dungeon Master's Assistant/, and I must say, it is coming along nicely. In case you are reading this, let me know, and I'll start to share it. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (when (f-directory? "~/other/rpgdm") (use-package rpgdm :straight (:local-repo "~/other/rpgdm") :commands (rpgdm-mode rpgdm-tables-load) :init (setq rpgdm-base (expand-file-name "~/other/rpgdm")) :config (ha-leader "t D" '("rpg dm" . rpgdm-mode)))) -#+END_SRC +#+end_src And my new Ironsworn project: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (when (f-directory? "~/other/emacs-ironsworn") (use-package rpgdm-ironsworn :after rpgdm @@ -76,14 +75,14 @@ And my new Ironsworn project: org-link-elisp-skip-confirm-regexp (rx string-start (optional "(") "rpgdm-" (or "tables-" "ironsworn-") (one-or-more any))))) -#+END_SRC +#+end_src * Technical Artifacts :noexport: Let's =provide= a name so we can =require= this file: -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none (provide 'ha-aux-apps) ;;; ha-aux-apps.el ends here - #+END_SRC + #+end_src #+DESCRIPTION: A literate programming file for helper apps in Emacs. diff --git a/ha-capturing-notes.org b/ha-capturing-notes.org index 492c678..00cede6 100644 --- a/ha-capturing-notes.org +++ b/ha-capturing-notes.org @@ -1,15 +1,14 @@ #+TITLE: Capturing Notes with Org #+AUTHOR: Howard X. Abrams #+DATE: 2020-09-18 -#+FILETAGS: :emacs: A literate programming file for configuring org for capturing notes. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; capturing-notes --- Configuring org for capturing notes. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,188 +22,183 @@ A literate programming file for configuring org for capturing notes. ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC +#+end_src * Introduction Capturing (or collecting) notes from files, browsers, and meetings, is a great way to get organized. I even have external commands that kick-off the capturing process, and without a command this is what gets called: - -#+BEGIN_SRC emacs-lisp -(setq org-capture-default-template "c") -#+END_SRC +#+begin_src emacs-lisp + (setq org-capture-default-template "c") +#+end_src Let's now define my templates. * Templates -Just make sure we can execute this code anytime, let's just define the variable that will hold all the templates: - -#+BEGIN_SRC emacs-lisp -(defvar org-capture-templates (list)) -#+END_SRC +To make sure we can execute this code anytime, let's define the variable that will hold all the templates: +#+begin_src emacs-lisp + (defvar org-capture-templates (list)) +#+end_src Some templates put the information /in front/ of other information (as opposed to the default of appending), so I define a helper function: -#+BEGIN_SRC emacs-lisp -(defun ha-first-header () - (goto-char (point-min)) - (search-forward-regexp "^\* ") - (beginning-of-line 1) - (point)) -#+END_SRC +#+begin_src emacs-lisp + (defun ha-first-header () + (goto-char (point-min)) + (search-forward-regexp "^\* ") + (beginning-of-line 1) + (point)) +#+end_src ** General Notes Capturing text into the =org-default-notes-file= is something I don't do much: -#+BEGIN_SRC emacs-lisp - (add-to-list 'org-capture-templates - '("n" "Thought or Note" entry - (file org-default-notes-file) - "* %?\n\n %i\n\n See: %a" :empty-lines 1)) - (add-to-list 'org-capture-templates - '("w" "Website Announcement" entry - (file+function "~/website/index.org" ha-first-header) - (file "~/.spacemacs.d/templates/website-announcement.org") - :empty-lines 1)) -#+END_SRC +#+begin_src emacs-lisp + (add-to-list 'org-capture-templates + '("n" "Thought or Note" entry + (file org-default-notes-file) + "* %?\n\n %i\n\n See: %a" :empty-lines 1)) + (add-to-list 'org-capture-templates + '("w" "Website Announcement" entry + (file+function "~/website/index.org" ha-first-header) + (file "~/.spacemacs.d/templates/website-announcement.org") + :empty-lines 1)) +#+end_src Before we go too far, we should create a publishing file for the website announcement, and something for the journal. ** Clock in Tasks -Org has one task at a time that can be /clocked in/ keeping a timer. I use that as a /destination/ for collecting notes. For instance, capturing with a =c= allows me to just enter stuff in under that task without switching to it: +Org has one task at a time that can be /clocked in/ keeping a timer. I use that as a /destination/ for collecting notes. For instance, capturing with a =c= allows me to just enter details under that task without switching to it: -#+BEGIN_SRC emacs-lisp -(add-to-list 'org-capture-templates - '("c" "Currently clocked in task")) -#+END_SRC +#+begin_src emacs-lisp + (add-to-list 'org-capture-templates + '("c" "Currently clocked in task")) +#+end_src Let's put a bullet item under that task: -#+BEGIN_SRC emacs-lisp -(add-to-list 'org-capture-templates - `("cc" "Item to Current Clocked Task" item - (clock) - "%i%?" :empty-lines 1)) -#+END_SRC +#+begin_src emacs-lisp + (add-to-list 'org-capture-templates + `("cc" "Item to Current Clocked Task" item + (clock) + "%i%?" :empty-lines 1)) +#+end_src We can select a /region/ and copy that using =c r=: -#+BEGIN_SRC emacs-lisp -(add-to-list 'org-capture-templates - `("cr" "Contents to Current Clocked Task" plain - (clock) - "%i" :immediate-finish t :empty-lines 1)) -#+END_SRC +#+begin_src emacs-lisp + (add-to-list 'org-capture-templates + `("cr" "Contents to Current Clocked Task" plain + (clock) + "%i" :immediate-finish t :empty-lines 1)) +#+end_src If we have copied anything into the clipboard, that information can be add to the current task using =c k=: - -#+BEGIN_SRC emacs-lisp -(add-to-list 'org-capture-templates - `("ck" "Kill-ring to Current Clocked Task" plain - (clock) - "%c" :immediate-finish t :empty-lines 1)) -#+END_SRC +#+begin_src emacs-lisp + (add-to-list 'org-capture-templates + `("ck" "Kill-ring to Current Clocked Task" plain + (clock) + "%c" :immediate-finish t :empty-lines 1)) +#+end_src Instead, if I am looking at some code, I can copy some code from a region, but use a helper function to create a /link/ to the original source code using =c f=: - -#+BEGIN_SRC emacs-lisp -(add-to-list 'org-capture-templates - `("cf" "Code Reference with Comments to Current Task" - plain (clock) - "%(ha-org-capture-code-snippet \"%F\")\n\n %?" - :empty-lines 1)) -#+END_SRC +#+begin_src emacs-lisp + (add-to-list 'org-capture-templates + `("cf" "Code Reference with Comments to Current Task" + plain (clock) + "%(ha-org-capture-code-snippet \"%F\")\n\n %?" + :empty-lines 1)) +#+end_src If I want a reference to the code, without any comments, I call ~c l~: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (add-to-list 'org-capture-templates `("cl" "Link to Code Reference to Current Task" plain (clock) "%(ha-org-capture-code-snippet \"%F\")" :empty-lines 1 :immediate-finish t)) -#+END_SRC +#+end_src ** Capture Helper Functions -In order to have a capture back-ref to a function and its code, we need to use this: - -#+BEGIN_SRC emacs-lisp -(require 'which-func) -#+END_SRC +To have a capture back-ref to a function and its code, we need to use this: +#+begin_src emacs-lisp + (require 'which-func) +#+end_src This helper function given a code /type/ and the /function/, analyzes the current buffer in order to collects data about the source code file. It then creates a nice-looking template: -#+BEGIN_SRC emacs-lisp -(defun ha-org-capture-fileref-snippet (f type headers func-name) - (let* ((code-snippet - (buffer-substring-no-properties (mark) (- (point) 1))) - (file-name (buffer-file-name)) - (file-base (file-name-nondirectory file-name)) - (line-number (line-number-at-pos (region-beginning))) - (initial-txt (if (null func-name) - (format "From [[file:%s::%s][%s]]:" - file-name line-number file-base) - (format "From ~%s~ (in [[file:%s::%s][%s]]):" - func-name file-name line-number - file-base)))) - (format " - %s +#+begin_src emacs-lisp + (defun ha-org-capture-fileref-snippet (f type headers func-name) + (let* ((code-snippet + (buffer-substring-no-properties (mark) (- (point) 1))) + (file-name (buffer-file-name)) + (file-base (file-name-nondirectory file-name)) + (line-number (line-number-at-pos (region-beginning))) + (initial-txt (if (null func-name) + (format "From [[file:%s::%s][%s]]:" + file-name line-number file-base) + (format "From ~%s~ (in [[file:%s::%s][%s]]):" + func-name file-name line-number + file-base)))) + (format " + %s - #+BEGIN_%s %s -%s - #+END_%s" initial-txt type headers code-snippet type))) -#+END_SRC + ,#+BEGIN_%s %s + %s + ,#+END_%s" initial-txt type headers code-snippet type))) +#+end_src -For typical code references, we can get the label for Org's =SRC= block by taking the =major-mode= and removing the =-mode= part. We can then call the formatter we previously defined: +For typical code references, we can get the label for Org's =SRC= block by taking the =major-mode= and removing the =-mode= part. We can then call the formatter previously defined: -#+BEGIN_SRC emacs-lisp -(defun ha-org-capture-code-snippet (f) - "Given a file, F, this captures the currently selected text -within an Org SRC block with a language based on the current mode -and a backlink to the function and the file." - (with-current-buffer (find-buffer-visiting f) - (let ((org-src-mode (replace-regexp-in-string "-mode" "" (format "%s" major-mode))) - (func-name (which-function))) - (ha-org-capture-fileref-snippet f "SRC" org-src-mode func-name)))) -#+END_SRC +#+begin_src emacs-lisp + (defun ha-org-capture-code-snippet (f) + "Given a file, F, this captures the currently selected text + within an Org SRC block with a language based on the current mode + and a backlink to the function and the file." + (with-current-buffer (find-buffer-visiting f) + (let ((org-src-mode (replace-regexp-in-string "-mode" "" (format "%s" major-mode))) + (func-name (which-function))) + (ha-org-capture-fileref-snippet f "SRC" org-src-mode func-name)))) +#+end_src Let's assume that we want to copy some text from a file, but it isn't source code, then this function makes an =EXAMPLE= of it. -#+BEGIN_SRC emacs-lisp -(defun ha-org-capture-clip-snippet (f) - "Given a file, F, this captures the currently selected text -within an Org EXAMPLE block and a backlink to the file." - (with-current-buffer (find-buffer-visiting f) - (ha-org-capture-fileref-snippet f "EXAMPLE" "" nil))) -#+END_SRC +#+begin_src emacs-lisp + (defun ha-org-capture-clip-snippet (f) + "Given a file, F, this captures the currently selected text + within an Org EXAMPLE block and a backlink to the file." + (with-current-buffer (find-buffer-visiting f) + (ha-org-capture-fileref-snippet f "EXAMPLE" "" nil))) +#+end_src ** Code Capturing Functions -In order to easily call a capture for code, let's make two interactive functions, one just copies the stuff, and the other pulls up a capturing window for comments: +To easily call a capture for code, let's make two interactive functions, one just copies the stuff, and the other pulls up a capturing window for comments: -#+BEGIN_SRC emacs-lisp -(defun ha-code-to-clock (&optional start end) - "Send the currently selected code to the currently clocked-in org-mode task." - (interactive) - (org-capture nil "F")) +#+begin_src emacs-lisp + (defun ha-code-to-clock (&optional start end) + "Send the currently selected code to the currently clocked-in org-mode task." + (interactive) + (org-capture nil "F")) -(defun ha-code-comment-to-clock (&optional start end) - "Send the currently selected code (with comments) to the -currently clocked-in org-mode task." - (interactive) - (org-capture nil "f")) -#+END_SRC + (defun ha-code-comment-to-clock (&optional start end) + "Send the currently selected code (with comments) to the + currently clocked-in org-mode task." + (interactive) + (org-capture nil "f")) +#+end_src * External Capturing If we put something on the clipboard using =xclip= or something, and then perhaps =emacsclient= could call this function to put those contents into clocked in task. -#+BEGIN_SRC emacs-lisp -(defun ha-external-capture-to-org () - "Calls `org-capture-string' on the contents of the Apple clipboard." - (interactive) - (org-capture-string (ha-org-clipboard) "ck") - (ignore-errors - (delete-frame))) -#+END_SRC +#+begin_src emacs-lisp + (defun ha-external-capture-to-org () + "Calls `org-capture-string' on the contents of the Apple clipboard." + (interactive) + (org-capture-string (ha-org-clipboard) "ck") + (ignore-errors + (delete-frame))) +#+end_src The =en= script is used as the last pipe entry on the command line, this displays the output, and then copies the contents into the Emacs-based engineering notebook at the currently clocked in task. -#+BEGIN_SRC shell :shebang "#!/bin/bash" :tangle ~/bin/en +#+begin_src shell :shebang "#!/bin/bash" :tangle ~/bin/en # Interface to my Engineering Notebook. # # Used as the last pipe entry on the command line, this displays the output, @@ -292,10 +286,10 @@ The =en= script is used as the last pipe entry on the command line, this display emacsclient -s work -e '(org-capture-string "" "ck")' rm -f $FILE -#+END_SRC +#+end_src * Keybindings Along with kicking off the org-capture, I want to be able to clock-in and out: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (with-eval-after-load 'ha-org (ha-org-leader "X" '("org capture" . org-capture) @@ -313,15 +307,15 @@ Along with kicking off the org-capture, I want to be able to clock-in and out: "c t" '("eval range" . org-evaluate-time-range) "c =" '("timestamp up" . org-clock-timestamps-up) "c -" '("timestamp down" . org-clock-timestamps-down))) -#+END_SRC +#+end_src * Technical Artifacts :noexport: Let's provide a name so we can =require= this file. -#+BEGIN_SRC emacs-lisp :exports none -(provide 'ha-capturing-notes) -;;; ha-capturing-notes.el ends here -#+END_SRC +#+begin_src emacs-lisp :exports none + (provide 'ha-capturing-notes) + ;;; ha-capturing-notes.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~ diff --git a/ha-config.org b/ha-config.org index 62ce49c..eed664f 100644 --- a/ha-config.org +++ b/ha-config.org @@ -1,15 +1,14 @@ #+TITLE: General Emacs Configuration #+AUTHOR: Howard X. Abrams #+DATE: 2020-09-10 -#+FILETAGS: :emacs: A literate programming file for configuring Emacs. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-config --- Emacs configuration. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,64 +22,63 @@ A literate programming file for configuring Emacs. ;; Using `find-file-at-point', and tangle the file to recreate this one . ;; ;;; Code: -#+END_SRC +#+end_src * Basic Configuration I hate a fat-finger that stop Emacs: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq confirm-kill-emacs 'yes-or-no-p) -#+END_SRC +#+end_src -New way to display line-numbers. I set mine to =relative= so that I can easily jump up and down by that value. Set this to =nil= to turn off, or =t= to be absolute. +New way to display line-numbers. I set mine to =relative= so that I can jump up and down by that value. Set this to =nil= to turn off, or =t= to be absolute. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq display-line-numbers t display-line-numbers-type 'relative) -#+END_SRC +#+end_src As [[https://philjackson.github.io//emacs/backups/2022/01/31/keeping-backups-of-every-edited-file/][Phil Jackson]] mentioned, Emacs has a lot of file backup strategy, and either change the [[help:backup-directory-alist][backup-directory-alist]] to put individual file backups elsewhere, e.g. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq backup-directory-alist `(("." . ,(concat user-emacs-directory "backups")))) -#+END_SRC +#+end_src Oh, and let’s see if I will use the =recentf= feature more: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (recentf-mode 1) -#+END_SRC +#+end_src -Or leave them in the current directory, but create an alias to =ls= to normally not see them, e.g. - -#+BEGIN_SRC sh +Or leave them in the current directory, but create an alias so =ls= doesn’t display them, e.g. +#+begin_src sh alias ls="ls --color=auto --hide='*~'" -#+END_SRC +#+end_src I'm leaving them side-by-side, but I am keeping some extra copies: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq create-lockfiles nil ; Having .# files around ain't helpful auto-save-default t delete-old-versions t kept-new-versions 6 kept-old-versions 2 version-control t) -#+END_SRC +#+end_src The [[help:version-control][version-control]] variable affect backups (not some sort of global VC setting), this makes numeric backups. I like the rendering to curved quotes using [[help:text-quoting-style][text-quoting-style]], because it improves the readability of documentation strings in the =∗Help∗= buffer and whatnot. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq text-quoting-style 'curve) -#+END_SRC +#+end_src Changes and settings I like introduced in Emacs 28: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq use-short-answers t describe-bindings-outline t completions-detailed t) -#+END_SRC +#+end_src As [[https://tecosaur.github.io/emacs-config/config.html][tec wrote]], I want to use =~/.authsource.gpg= as I don’t want to accidentaly purge this file cleaning =~/.emacs.d=, and let's cache as much as possible, as my home machine is pretty safe, and my laptop is shutdown a lot. Also, as [[https://www.bytedude.com/gpg-in-emacs/][bytedude]] mentions, I need to se the =epa-pineentry-mode= to =loopback= to actually get a prompt for the password, instead of an error. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package epa-file :config (defvar epa-pinentry-mode) @@ -88,71 +86,71 @@ As [[https://tecosaur.github.io/emacs-config/config.html][tec wrote]], I want to epa-pinentry-mode 'loopback auth-sources '("~/.authinfo.gpg") auth-source-cache-expiry nil)) -#+END_SRC +#+end_src More settings: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq truncate-string-ellipsis "…" ; Unicode ellispis are nicer than "..." debug-on-error t) -#+END_SRC +#+end_src -The venerable [[help:hippie-expand][hippie-expand]] function does a better job than the default, [[help:dabbrev-expand][dabbrev-expand]], so let’s swap it out (see this [[https://www.masteringemacs.org/article/text-expansion-hippie-expand][excellent essay]] by Mickey Petersen): -#+BEGIN_SRC emacs-lisp +The venerable [[help:hippie-expand][hippie-expand]] function does a better job than the default, [[help:dabbrev-expand][dabbrev-expand]], so let’s swap it out (see this [[https://www.masteringemacs.org/article/text-expansion-hippie-expand][essay]] by Mickey Petersen): +#+begin_src emacs-lisp (global-set-key [remap dabbrev-expand] 'hippie-expand) -#+END_SRC +#+end_src Details? Check out its [[help:hippie-expand-try-functions-list][list of expanders]]. -What would be /really nice/ is to have it bound to ~TAB~ instead of the default ~M-/~. Normally, ~TAB~ re-indents the line, but I find that I usually want that feature when I’m in Evil’s =normal state= and hit the ~=~ key, so changing this sounds good. But why not /have both/? -#+BEGIN_SRC emacs-lisp +Let’s bind ~TAB~ instead of the default ~M-/~. By default, ~TAB~ re-indents the line, but I find that I want that feature when I’m in Evil’s =normal state= and hit the ~=~ key, so changing this sounds good. But why not /have both/? +#+begin_src emacs-lisp (advice-add #'indent-for-tab-command :after #'hippie-expand) -#+END_SRC +#+end_src Now while we’re typing along, we can hit the ~TAB~ key after partially typing a word to have it completed. Save the file whenever I move away from Emacs (see [[https://irreal.org/blog/?p=10314][this essay]]): -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun save-all-buffers () "Saves all buffers, because, why not?" (interactive) (save-some-buffers t)) (add-hook 'focus-out-hook 'save-all-buffers) -#+END_SRC +#+end_src And some Mac-specific settings: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (when (ha-running-on-macos?) (setq mac-option-modifier 'meta mac-command-modifier 'super) (add-to-list 'default-frame-alist '(ns-transparent-titlebar . t)) (add-to-list 'default-frame-alist '(ns-appearance . dark))) -#+END_SRC +#+end_src * Support Packages ** Piper Rewriting my shell scripts in Emacs Lisp uses my [[https://gitlab.com/howardabrams/emacs-piper][emacs-piper project]], and this code spills into my configuration code, so let's load it now: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package piper :straight (:type git :protocol ssh :host gitlab :repo "howardabrams/emacs-piper") - :commands piper shell-command-to-list ; I use this function quite a bit + :commands piper shell-command-to-list ; I use this function often :bind (:map evil-normal-state-map ("C-M-|" . piper) ("C-|" . piper-user-interface))) -#+END_SRC +#+end_src ** Yet Another Snippet System (YASnippets) Using [[https://github.com/joaotavora/yasnippet][yasnippet]] to convert templates into text: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package yasnippet :config (add-to-list 'yas-snippet-dirs (expand-file-name "snippets" user-emacs-directory)) (yas-global-mode +1)) -#+END_SRC +#+end_src Check out [[http://joaotavora.github.io/yasnippet/][the documentation]] for writing them. Seems the best [[https://github.com/hlissner/doom-snippets][collection of snippets]] is what Henrik Lissner has made for Doom (otherwise, we should use [[http://github.com/AndreaCrotti/yasnippet-snippets][yasnippet-snippets]] package): -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package doom-snippets :after yasnippet :straight (:type git :protocol ssh :host github :repo "doomemacs/snippets") @@ -161,46 +159,45 @@ Seems the best [[https://github.com/hlissner/doom-snippets][collection of snippe (expand-file-name "straight") (expand-file-name "repos") (expand-file-name "doom-snippets")))) -#+END_SRC +#+end_src *Note:* Including his snippets also includes some [[https://github.com/hlissner/doom-snippets#snippets-api][helper functions]] and other features. ** Auto Insert Templates -The [[https://www.emacswiki.org/emacs/AutoInsertMode][auto-insert]] feature is a wee bit complicated. All I want is to associate a filename regular expression with a YASnippet template. I'm stealing some ideas from Henrik Lissner's [[https://github.com/hlissner/doom-emacs/blob/develop/modules/editor/file-templates/autoload.el][set-file-template!]] macro, but maybe simpler? -#+BEGIN_SRC emacs-lisp +The [[https://www.emacswiki.org/emacs/AutoInsertMode][auto-insert]] feature is a wee bit complicated. All I want is to associate a filename regular expression with a YASnippet template. I'm stealing some ideas from Henrik Lissner's [[https://github.com/hlissner/doom-emacs/blob/develop/modules/editor/file-templates/autoload.el][set-file-template!]] macro, but simpler? +#+begin_src emacs-lisp (use-package autoinsert :init (setq auto-insert-directory (expand-file-name "templates" user-emacs-directory)) - ;; Don't want to be prompted before insertion: + ;; Don't prompt before insertion: (setq auto-insert-query nil) (add-hook 'find-file-hook 'auto-insert) (auto-insert-mode t)) -#+END_SRC -However, auto insertion requires entering data for particular fields, and for that Yasnippet is better, so in this case, we combine them: -#+BEGIN_SRC emacs-lisp +#+end_src +Since auto insertion requires entering data for particular fields, and for that Yasnippet is better, so in this case, we combine them: +#+begin_src emacs-lisp (defun ha-autoinsert-yas-expand() "Replace text in yasnippet template." (yas-expand-snippet (buffer-string) (point-min) (point-max))) -#+END_SRC +#+end_src And since I'll be associating snippets with new files all over my configuration, let's make a helper function: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-auto-insert-file (filename-re snippet-name) "Autofill file buffer matching FILENAME-RE regular expression. The contents inserted from the YAS SNIPPET-NAME." ;; The define-auto-insert takes a regular expression and an ACTION: - ;; ACTION may also be a vector containing several successive single - ;; actions as described above, e.g. ["header.insert" author-update]. + ;; ACTION may also be a vector containing successive single actions. (define-auto-insert filename-re (vector snippet-name 'ha-autoinsert-yas-expand))) -#+END_SRC +#+end_src As an example of its use, any Org files loaded in /this project/ should insert my config file: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-auto-insert-file (rx "hamacs/" (one-or-more any) ".org" eol) "hamacs-config") -#+END_SRC +#+end_src ** Request System -The above code (and other stuff) needs the [[https://github.com/tkf/emacs-request][request]] package: -#+BEGIN_SRC emacs-lisp +The above code needs the [[https://github.com/tkf/emacs-request][request]] package: +#+begin_src emacs-lisp (use-package request :init (defvar ha-dad-joke nil "Holds the latest dad joke.") @@ -230,46 +227,46 @@ The above code (and other stuff) needs the [[https://github.com/tkf/emacs-reques :complete (cl-function (lambda (&key data &allow-other-keys) (setq ha-dad-joke data)))))) -#+END_SRC +#+end_src *** Dad Jokes! -The /critical part/ here, is the [[https://icanhazdadjoke.com/][Dad Joke]] function, which is just a =curl= call: -#+BEGIN_SRC sh +The /critical part/ here, is the [[https://icanhazdadjoke.com/][Dad Joke]] function, a =curl= call to a web service: +#+begin_src sh curl -sH "Accept: text/plain" https://icanhazdadjoke.com/ -#+END_SRC +#+end_src For this, I use the =request= package, which is /asynchronous/ -#+BEGIN_SRC emacs-lisp -#+END_SRC +#+begin_src emacs-lisp +#+end_src * Configuration Changes ** Initial Settings and UI -Let's turn off the menu and other things: -#+BEGIN_SRC emacs-lisp +Let's turn off the menu and other settings: +#+begin_src emacs-lisp (when (display-graphic-p) (tool-bar-mode -1) (scroll-bar-mode -1) (horizontal-scroll-bar-mode -1) (setq visible-bell 1)) -#+END_SRC +#+end_src I dislike forgetting to trim trailing white-space: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (add-hook 'before-save-hook 'delete-trailing-whitespace) -#+END_SRC +#+end_src I like being able to enable local variables in =.dir-local.el= files: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq enable-local-variables t) -#+END_SRC +#+end_src ** Completing Read User Interface After using Ivy, I am going the route of a =completing-read= interface that extends the original Emacs API, as opposed to implementing backend-engines or complete replacements. *** Vertico -The [[https://github.com/minad/vertico][vertico]] package puts the completing read in a vertical format, and seems to fit the bill. It seems to be similar to [[https://github.com/raxod502/selectrum#vertico][Selectrum]]. -#+BEGIN_SRC emacs-lisp +The [[https://github.com/minad/vertico][vertico]] package puts the completing read in a vertical format, and like [[https://github.com/raxod502/selectrum#vertico][Selectrum]], it extends Emacs’ built-in functionality, instead of adding a new process. This means all these projects work together. +#+begin_src emacs-lisp (use-package vertico :config (vertico-mode)) -#+END_SRC -My only issue with using Vertico with =find-file= is that I really like having the Return key insert the directory at point, and not open =dired=. Seems like this is addressed with this extension /installed with Vertico/: -#+BEGIN_SRC emacs-lisp +#+end_src +My issue with Vertico is when calling =find-file=, the Return key opens =dired=, instead of inserting the directory at point. This package addresses this: +#+begin_src emacs-lisp (use-package vertico-directory :straight (el-patch :files ("~/.emacs.d/straight/repos/vertico/extensions/vertico-directory.el")) ;; More convenient directory navigation commands @@ -280,20 +277,17 @@ My only issue with using Vertico with =find-file= is that I really like having t ("M-TAB" . minibuffer-complete)) ;; Tidy shadowed file names :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)) -#+END_SRC +#+end_src *** Hotfuzz -This fuzzy completion style is similar to the built-in =flex= style, but has a better scoring algorithm. Specifically, it is non-greedy and ranks completions that match at word; path component; or camelCase boundaries higher. +This fuzzy completion style is like the built-in =flex= style, but has a better scoring algorithm, non-greedy and ranks completions that match at word; path component; or camelCase boundaries higher. -#+BEGIN_SRC emacs-lisp - (use-package hotfuzz - :config - (setq completion-styles '(hotfuzz) - completion-ignore-case t)) -#+END_SRC -While it is very flexible at matching, you have to get the /order/ correct. For instance, ~alireg~ matches with [[help:align-regexp][align-regexp]], but ~regali~ does not. +#+begin_src emacs-lisp + (use-package hotfuzz) +#+end_src +While flexible at matching, you have to get the /order/ correct. For instance, ~alireg~ matches with [[help:align-regexp][align-regexp]], but ~regali~ does not, so we will use =hotfuzz= for scoring, and not use this as a completion-project (see the =fussy= project below). *** Orderless While the space can be use to separate words (acting a bit like a =.*= regular expression), the [[https://github.com/oantolin/orderless][orderless]] project allows those words to be in any order. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package orderless :commands (orderless-filter) :custom @@ -301,14 +295,14 @@ While the space can be use to separate words (acting a bit like a =.*= regular e (completion-ignore-case t) (completion-category-defaults nil) (completion-category-overrides '((file (styles partial-completion))))) -#+END_SRC -*Note:* Multiple files can be opened at once with =find-file= if you enter a wildcard. We may also give the =initials= completion style a try. +#+end_src +*Note:* Open more than one file at once with =find-file= with a wildcard. We may also give the =initials= completion style a try. *** Fussy Filtering and Matching -The [[https://github.com/jojojames/fussy][fussy]] project is a fuzzy pattern matching extension for the normal [[help:completing-read][completing-read]] interface. By default, it uses [[https://github.com/lewang/flx][flx]], however, its goal is to allow multiple sorting and filtering algorithms to be used. +The [[https://github.com/jojojames/fussy][fussy]] project is a fuzzy pattern matching extension for the normal [[help:completing-read][completing-read]] interface. By default, it uses [[https://github.com/lewang/flx][flx]], but we can specify other sorting and filtering algorithms. How does it compare? Once upon a time, I enjoyed typing ~plp~ for =package-list-packages=, and when I switched to [[https://github.com/oantolin/orderless][orderless]], I would need to put a space between the words. While I will continue to play with the different mechanism, I’ll combine =hotfuzz= and =orderless=. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package fussy :straight (:host github :repo "jojojames/fussy") :config @@ -317,46 +311,46 @@ How does it compare? Once upon a time, I enjoyed typing ~plp~ for =package-list- completion-category-overrides nil fussy-filter-fn 'fussy-filter-orderless-flex fussy-score-fn 'fussy-hotfuzz-score)) -#+END_SRC +#+end_src *** Savehist Persist history over Emacs restarts using the built-in [[https://www.emacswiki.org/emacs/SaveHist][savehist]] project. Since both Vertico and Selectrum sorts by history position, this should make the choice /smarter/ with time. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package savehist :init (savehist-mode)) -#+END_SRC +#+end_src *** Marginalia The [[https://github.com/minad/marginalia][marginalia]] package gives a preview of =M-x= functions with a one line description, extra information when selecting files, etc. Nice enhancement without learning any new keybindings. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp ;; Enable richer annotations using the Marginalia package (use-package marginalia :init (setq marginalia-annotators-heavy t) :config (marginalia-mode)) -#+END_SRC +#+end_src * Key Bindings To begin my binding changes, let's turn on [[https://github.com/justbur/emacs-which-key][which-key]]: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package which-key :init (setq which-key-popup-type 'minibuffer) :config (which-key-mode)) -#+END_SRC +#+end_src Why would I ever quit Emacs with a simple keybinding? Let’s override it: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (global-set-key (kbd "s-q") 'bury-buffer) -#+END_SRC +#+end_src Oh, and let’s not close the frame, but instead, the window: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (global-set-key (kbd "s-w") 'delete-window) -#+END_SRC +#+end_src ** Undo -I mean, I /always/ use ~C-/~ for [[help:undo][undo]] (and ~C-?~ for [[help:undo-redo][redo]]), but since I’m on the Mac quite a bit, I want to cover my bases. +I mean, I /always/ use ~C-/~ for [[help:undo][undo]] (and ~C-?~ for [[help:undo-redo][redo]]), but when I’m on the Mac, I need to cover my bases. Why use [[https://gitlab.com/ideasman42/emacs-undo-fu][undo-fu]] instead of the built-in undo functionality? Well, there isn’t much to the project (that’s a good thing), but It basically doesn’t /cycle/ around the redo, which annoying. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package undo-fu :config (global-set-key [remap undo] 'undo-fu-only-undo) @@ -364,14 +358,14 @@ Why use [[https://gitlab.com/ideasman42/emacs-undo-fu][undo-fu]] instead of the (global-unset-key (kbd "s-z")) (global-set-key (kbd "s-z") 'undo-fu-only-undo) (global-set-key (kbd "s-S-z") 'undo-fu-only-redo)) -#+END_SRC +#+end_src ** Evil-Specific Keybindings Can we change Evil at this point? Some tips: - [[https://github.com/noctuid/evil-guide]] - [[https://nathantypanski.com/blog/2014-08-03-a-vim-like-emacs-config.html]] - [[https://stackoverflow.com/questions/25542097/emacs-evil-mode-how-to-change-insert-state-to-emacs-state-automatically][Evil insert state is really Emacs?]] Real answer to that is to set [[help:evil-disable-insert-state-bindings][evil-disable-insert-state-bindings]] -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package evil :init (setq evil-undo-system 'undo-fu @@ -392,7 +386,7 @@ Can we change Evil at this point? Some tips: (define-key evil-insert-state-map (kbd "C-o") 'evil-execute-in-normal-state) (evil-mode)) -#+END_SRC +#+end_src While I’m pretty good with the VIM keybindings, I would like to play around with the text objects and how it compares to others (including the surround), for instance: - ~diw~ :: deletes a word, but can be anywhere in it, while ~de~ deletes to the end of the word. @@ -403,20 +397,20 @@ While I’m pretty good with the VIM keybindings, I would like to play around wi - Surrounding punctuation, like quotes, parenthesis, brackets, etc. Also work, so ~ci)~ changes all the parameters to a function call. Using the key-chord project allows me to make Escape be on two key combo presses on both sides of my keyboard: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package key-chord :config (key-chord-mode t) (key-chord-define-global "fd" 'evil-normal-state) (key-chord-define-global "jk" 'evil-normal-state) (key-chord-define-global "JK" 'evil-normal-state)) -#+END_SRC +#+end_src ** General Leader Key Sequences The one thing that both Spacemacs and Doom taught me, is how much I like the /key sequences/ that begin with a leader key. In both of those systems, the key sequences begin in the /normal state/ with a space key. This means, while typing in /insert state/, I have to escape to /normal state/ and then hit the space. I'm not trying an experiment where specially-placed function keys on my fancy ergodox keyboard can kick these off using [[https://github.com/noctuid/general.el][General Leader]] project. Essentially, I want a set of leader keys for Evil's /normal state/ as well as a global leader in all modes. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package general :custom (general-use-package-emit-autoloads t) @@ -436,10 +430,10 @@ I'm not trying an experiment where specially-placed function keys on my fancy er :prefix "SPC m" :global-prefix "" :non-normal-prefix "S-SPC")) -#+END_SRC +#+end_src *** Top-Level Operations Let's try this general "space" prefix by defining some top-level operations, including hitting ~space~ twice to bring up the =M-x= collection of functions: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "SPC" '("M-x" . execute-extended-command) "." '("repeat" . repeat) @@ -452,9 +446,9 @@ Let's try this general "space" prefix by defining some top-level operations, inc "o i" 'imenu "m" '(:ignore t :which-key "mode") "u" 'universal-argument) -#+END_SRC +#+end_src And ways to stop the system: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "q" '(:ignore t :which-key "quit/session") "q b" '("bury buffer" . bury-buffer) @@ -462,49 +456,48 @@ And ways to stop the system: "q K" '("kill emacs (and dæmon)" . save-buffers-kill-emacs) "q q" '("quit emacs" . save-buffers-kill-terminal) "q Q" '("quit without saving" . evil-quit-all-with-error-code)) -#+END_SRC +#+end_src *** File Operations -Obviously, =find-file= is still my bread and butter, but I do like getting information about the file associated with the buffer. For instance, the file path: -#+BEGIN_SRC emacs-lisp -(defun ha-relative-filepath (filepath) - "Return the FILEPATH without the HOME directory and typical filing locations. -The expectation is that this will return a filepath with the proejct name." - (let* ((home-re (rx (literal (getenv "HOME")) "/")) - (work-re (rx (regexp home-re) - (or "work" "other" "projects") ; Typical organization locations - "/" - (optional (or "4" "5" "xway") "/") ; Sub-organization locations - ))) - (cond - ((string-match work-re filepath) (substring filepath (match-end 0))) - ((string-match home-re filepath) (substring filepath (match-end 0))) - (t filepath)))) +While =find-file= is still my bread and butter, I like getting information about the file associated with the buffer. For instance, the file path: +#+begin_src emacs-lisp + (defun ha-relative-filepath (filepath) + "Return the FILEPATH without the HOME directory and typical filing locations. + The expectation is that this will return a filepath with the proejct name." + (let* ((home-re (rx (literal (getenv "HOME")) "/")) + (work-re (rx (regexp home-re) + (or "work" "other" "projects") ; Typical organization locations + "/" + (optional (or "4" "5" "xway") "/") ; Sub-organization locations + ))) + (cond + ((string-match work-re filepath) (substring filepath (match-end 0))) + ((string-match home-re filepath) (substring filepath (match-end 0))) + (t filepath)))) -(defun ha-yank-buffer-path (&optional root) - "Copy the file path of the buffer relative to my 'work' directory, ROOT." - (interactive) - (if-let (filename (buffer-file-name (buffer-base-buffer))) - (message "Copied path to clipboard: %s" - (kill-new (abbreviate-file-name - (if root - (file-relative-name filename root) - (ha-relative-filepath filename))))) - (error "Couldn't find filename in current buffer"))) + (defun ha-yank-buffer-path (&optional root) + "Copy the file path of the buffer relative to my 'work' directory, ROOT." + (interactive) + (if-let (filename (buffer-file-name (buffer-base-buffer))) + (message "Copied path to clipboard: %s" + (kill-new (abbreviate-file-name + (if root + (file-relative-name filename root) + (ha-relative-filepath filename))))) + (error "Couldn't find filename in current buffer"))) -(defun ha-yank-project-buffer-path (&optional root) - "Copy the file path of the buffer relative to the file's project. -If ROOT is given, they copies the filepath relative to that." - (interactive) - (if-let (filename (buffer-file-name (buffer-base-buffer))) - (message "Copied path to clipboard: %s" - (kill-new - (f-relative filename (or root (projectile-project-root filename))))) - (error "Couldn't find filename in current buffer"))) -#+END_SRC + (defun ha-yank-project-buffer-path (&optional root) + "Copy the file path of the buffer relative to the file's project. + When given ROOT, this copies the filepath relative to that." + (interactive) + (if-let (filename (buffer-file-name (buffer-base-buffer))) + (message "Copied path to clipboard: %s" + (kill-new + (f-relative filename (or root (projectile-project-root filename))))) + (error "Couldn't find filename in current buffer"))) +#+end_src -Perhaps my OCD is out-of-control, but I really want to load a file in another window, but want to control which window. - -#+BEGIN_SRC emacs-lisp +Perhaps my OCD is out-of-control, but I want to load a file in another window, but want to control which window. +#+begin_src emacs-lisp (defmacro ha-create-find-file-window (winum) (let ((func-name (intern (format "ha-find-file-window-%s" winum))) (call-func (intern (format "winum-select-window-%s" winum)))) @@ -516,10 +509,10 @@ Perhaps my OCD is out-of-control, but I really want to load a file in another wi (dolist (winum (number-sequence 1 9)) (ha-create-find-file-window winum)) -#+END_SRC +#+end_src With these helper functions in place, I can create a leader collection for file-related functions: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "f" '(:ignore t :which-key "files") "f f" '("load" . find-file) @@ -543,20 +536,20 @@ With these helper functions in place, I can create a leader collection for file- "f 7" '("load win-7" . ha-find-file-window-7) "f 8" '("load win-8" . ha-find-file-window-8) "f 9" '("load win-9" . ha-find-file-window-9)) -#+END_SRC +#+end_src *** Buffer Operations This section groups buffer-related operations under the "SPC b" sequence. Putting the entire visible contents of the buffer on the clipboard is often useful: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-yank-buffer-contents () "Copy narrowed contents of the buffer to the clipboard." (interactive) (kill-new (buffer-substring-no-properties (point-min) (point-max)))) -#+END_SRC +#+end_src And the collection of useful operations: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "b" '(:ignore t :which-key "buffers") "b B" '("switch" . persp-switch-to-buffer) @@ -579,10 +572,10 @@ And the collection of useful operations: ;; And double up on the bookmarks: "b m" '("set bookmark" . bookmark-set) "b M" '("delete mark" . bookmark-delete)) -#+END_SRC +#+end_src *** Toggle Switches The goal here is toggle switches and other miscellaneous settings. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "t" '(:ignore t :which-key "toggles") "t a" '("abbrev" . abbrev-mode) @@ -592,24 +585,24 @@ The goal here is toggle switches and other miscellaneous settings. "t t" '("truncate" . toggle-truncate-lines) "t v" '("visual" . visual-line-mode) "t w" '("whitespace" . whitespace-mode)) -#+END_SRC +#+end_src **** Line Numbers -Really? We can't automatically toggle between relative and absolute line numbers? -#+BEGIN_SRC emacs-lisp +Since we can't automatically toggle between relative and absolute line numbers, we create this function: +#+begin_src emacs-lisp (defun ha-toggle-relative-line-numbers () (interactive) (if (eq display-line-numbers 'relative) (setq display-line-numbers t) (setq display-line-numbers 'relative))) -#+END_SRC +#+end_src Add it to the toggle menu: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "t r" '("relative lines" . ha-toggle-relative-line-numbers)) -#+END_SRC +#+end_src **** Narrowing I like the focus the [[info:emacs#Narrowing][Narrowing features]] offer, but what a /dwim/ aspect: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-narrow-dwim () "Narrow to region or org-tree or widen if already narrowed." (interactive) @@ -621,24 +614,24 @@ I like the focus the [[info:emacs#Narrowing][Narrowing features]] offer, but wha (logos-narrow-dwim)) ((eq major-mode 'org-mode) (org-narrow-to-subtree)) (t (narrow-to-defun)))) -#+END_SRC +#+end_src And put it on the toggle menu: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "t n" '("narrow" . ha-narrow-dwim)) -#+END_SRC +#+end_src *** Window Operations While it comes with Emacs, I use [[https://www.emacswiki.org/emacs/WinnerMode][winner-mode]] to undo window-related changes: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package winner :custom (winner-dont-bind-my-keys t) :config (winner-mode +1)) -#+END_SRC +#+end_src Use the [[https://github.com/abo-abo/ace-window][ace-window]] project to jump to any window you see: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package ace-window) -#+END_SRC +#+end_src This package, bound to ~SPC w w~, also allows operations specified before choosing the window: - ~x~ - delete window - ~m~ - swap windows @@ -647,21 +640,21 @@ This package, bound to ~SPC w w~, also allows operations specified before choosi - ~j~ - select buffer - ~n~ - select the previous window - ~u~ - select buffer in the other window - - ~c~ - split window fairly, either vertically or horizontally + - ~c~ - split window, either vertically or horizontally - ~v~ - split window vertically - ~b~ - split window horizontally - ~o~ - maximize current window - ~?~ - show these command bindings -Keep in mind, these shortcuts only work with lots of windows open. For instance, ~SPC w w x 3~ closes the "3" window. +Keep in mind, these shortcuts work with more than two windows open. For instance, ~SPC w w x 3~ closes the "3" window. To jump to a window even quicker, use the [[https://github.com/deb0ch/emacs-winum][winum package]]: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package winum :config (winum-mode +1)) -#+END_SRC +#+end_src And when creating new windows, why isn't the new window selected? -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun jump-to-new-window (&rest _arg) "Advice function to jump to newly spawned window." (other-window 1)) @@ -669,18 +662,18 @@ And when creating new windows, why isn't the new window selected? (dolist (command '(split-window-below split-window-right evil-window-split evil-window-vsplit)) (advice-add command :after #'jump-to-new-window)) -#+END_SRC -This is nice since the window numbers are always present on a Doom modeline, however, they order the window numbers /differently/ than =ace-window=. Let's see which I end up liking better. +#+end_src +This is nice since the window numbers are always present on a Doom modeline, but they order the window numbers /differently/ than =ace-window=. Let's see which I end up liking better. The ~0~ key/window should be always associated with a project-specific tree window: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (add-to-list 'winum-assign-functions (lambda () (when (string-match-p (buffer-name) ".*\\*NeoTree\\*.*") 10))) -#+END_SRC +#+end_src -Let's try this out with a Hydra since some commands (enlarge window), I want to repeatedly call. It also allows me to organize the helper text. -#+BEGIN_SRC emacs-lisp +Let's try this out with a Hydra since some I can /repeat/ some commands (e.g. enlarge window). It also allows me to organize the helper text. +#+begin_src emacs-lisp (use-package hydra :config (defhydra hydra-window-resize (:color blue :hint nil) " @@ -698,7 +691,7 @@ Let's try this out with a Hydra since some commands (enlarge window), I want to ("d" delete-window) ("D" ace-delete-window) - ("z" ace-window-swap) + ("z" ace-swap-window) ("u" winner-undo) ("U" winner-undo :color pink) ("C-r" winner-redo) @@ -748,11 +741,11 @@ Let's try this out with a Hydra since some commands (enlarge window), I want to ("q" nil :color blue))) (ha-leader "w" '("windows" . hydra-window-resize/body)) -#+END_SRC +#+end_src *** Search Operations Ways to search for information goes under the ~s~ key. This primarily depends on the [[https://github.com/dajva/rg.el][rg]] package, which builds on the internal =grep= system, and creates a =*rg*= window with =compilation= mode, so ~C-j~ and ~C-k~ will move and show the results by loading those files. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package rg :init ; I sometimes call `grep`: ; (grep-apply-setting 'grep-command "rg -n -H --no-heading -e ") @@ -796,52 +789,43 @@ Ways to search for information goes under the ~s~ key. This primarily depends on (ha-rg-go-results-buffer) (previous-error-no-select) (compile-goto-error))) -#+END_SRC +#+end_src -The [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep package]] integrates with ripgrep. Typically, you can just his ~i~ to automatically go into =wgrep-mode= and edit away, however, I typically want to edit everything at the same time, so I have a toggle that should work as well: +The [[https://github.com/mhayashi1120/Emacs-wgrep][wgrep package]] integrates with =ripgrep=. Typically, you hit ~i~ to automatically go into =wgrep-mode= and edit away, but since I typically want to edit everything at the same time, I have a toggle that should work as well: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package wgrep :after rg :commands wgrep-rg-setup :hook (rg-mode-hook . wgrep-rg-setup) :config (ha-leader - :keymaps 'rg-mode-map ; Actually, just `i` works! + :keymaps 'rg-mode-map ; Actually, `i` works! "s w" '("wgrep-mode" . wgrep-change-to-wgrep-mode) "t w" '("wgrep-mode" . wgrep-change-to-wgrep-mode))) -#+END_SRC +#+end_src *** Text Operations Stealing much of this from Spacemacs. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "x" '(:ignore t :which-key "text") "x a" '("align" . align-regexp) "x q" '("fill paragraph" . fill-paragraph) "x p" '("unfill paragraph" . unfill-paragraph)) -#+END_SRC -Unfilling a paragraph joins all the lines in a paragraph into a single line. Taken [[http://www.emacswiki.org/UnfillParagraph][from here]] ... I use this all the time: +#+end_src +Unfilling a paragraph joins all the lines in a paragraph into a single line. Taken [[http://www.emacswiki.org/UnfillParagraph][from here]] … I use this all the time: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun unfill-paragraph () "Convert a multi-line paragraph into a single line of text." (interactive) (let ((fill-column (point-max))) (fill-paragraph nil))) -#+END_SRC +#+end_src *** Help Operations While the ~C-h~ is easy enough, I am now in the habit of typing ~SPC h~ instead. -Sure, I believe I should have grabbed /all/ the help functions: -#+BEGIN_SRC emacs-lisp :tangle no - (ha-leader "h" `("help" . ,(lookup-key global-map (kbd "C-h")))) -#+END_SRC -Or simply connect the =help-map= (not that we wants it /value/): -#+BEGIN_SRC emacs-lisp :tangle no - (ha-leader "'" `("help" . ,help-map)) -#+END_SRC - -Since I decided to tweak the help menu, so I craft my own: -#+BEGIN_SRC emacs-lisp +Since I tweaked the help menu, I craft my own menu: +#+begin_src emacs-lisp (ha-leader "h" '(:ignore t :which-key "help") "h a" '("apropos" . apropos-command) @@ -860,14 +844,14 @@ Since I decided to tweak the help menu, so I craft my own: "h i" '("info" . info) "h I" '("info manual" . info-display-manual) "h j" '("info jump" . info-apropos)) -#+END_SRC +#+end_src Remember these keys in the *Help* buffer: - ~s~ :: view source of the function - ~i~ :: view info manual of the function Let's make Info behave a little more VI-like: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package info :straight (:type built-in) :general @@ -881,10 +865,10 @@ Let's make Info behave a little more VI-like: "T" 'Info-top-node "p" 'Info-backward-node "n" 'Info-forward-node)) ; Old habit die hard -#+END_SRC +#+end_src *** Consult -The [[https://github.com/minad/consult][consult project]] aims to use the libraries like [[*Vertico][Vertico]] to enhance specific, built-in, Emacs functions. I particularly appreciate the feature that when selecting an element in the minibuffer, it displays what you are looking at… for instance, it previews a buffer before choosing it. Unlike /Vertico/ and /Orderless/, you need to bind keys to its special functions (or rebind existing keys that do something similar). -#+BEGIN_SRC emacs-lisp +The [[https://github.com/minad/consult][consult project]] aims to use libraries like [[*Vertico][Vertico]] to enhance specific, built-in, Emacs functions. I appreciate this project that when selecting an element in the minibuffer, it displays what you are looking at… for instance, it previews a buffer before choosing it. Unlike /Vertico/ and /Orderless/, you need to bind keys to its special functions (or rebind existing keys that do something similar). +#+begin_src emacs-lisp (use-package consult :after general ;; Enable automatic preview at point in the *Completions* buffer. This is @@ -907,10 +891,10 @@ The [[https://github.com/minad/consult][consult project]] aims to use the librar (:states 'normal "gp" 'consult-yank-pop "gs" 'consult-line)) -#+END_SRC +#+end_src *** Consult for Projects -One of the reasons that Consult hasn’t been too important to me, is that I often narrow my searching based on projectile. So let’s see what the [[https://gitlab.com/OlMon/consult-projectile][consult-projectile]] can offer. -#+BEGIN_SRC emacs-lisp +One of the reasons that Consult hasn’t been too important to me, is that I often narrow my searching based on projectile. The [[https://gitlab.com/OlMon/consult-projectile][consult-projectile]] can help with this. +#+begin_src emacs-lisp (use-package consult-projectile :after consult general :straight (consult-projectile :type git :host gitlab :repo "OlMon/consult-projectile" :branch "master") @@ -921,12 +905,11 @@ One of the reasons that Consult hasn’t been too important to me, is that I oft "p p" '("switch project" . consult-projectile-switch-project) "p f" '("find file" . consult-projectile-find-file) "p r" '("find recent file" . consult-projectile-recentf))) - -#+END_SRC +#+end_src The advantage of [[help:persp-switch-to-buffer][persp-switch-to-buffer]] over =consult-projectile-switch-to-buffer= is that is shows non-file buffers. *** Embark -The [[https://github.com/oantolin/embark/][embark]] project offers /actions/ on /targets/, however, I'm primarily thinking of acting on selected items in the minibuffer, however, they actually act anywhere. Consequently, I need an easy-to-use keybinding that doesn't conflict. Hey, that is what the Super key is for, right? -#+BEGIN_SRC emacs-lisp +The [[https://github.com/oantolin/embark/][embark]] project offers /actions/ on /targets/. I'm primarily thinking of acting on selected items in the minibuffer, but these commands act anywhere. I need an easy-to-use keybinding that doesn't conflict. Hey, that is what the Super key is for, right? +#+begin_src emacs-lisp (use-package embark :bind (("s-;" . embark-act) ; Work in minibuffer and elsewhere @@ -938,11 +921,11 @@ The [[https://github.com/oantolin/embark/][embark]] project offers /actions/ on :config (ha-leader "h K" '("keybindings" . embark-bindings))) -#+END_SRC +#+end_src According to [[https://elpa.gnu.org/packages/embark-consult.html#orgc76b5de][this essay]], Embark cooperates well with the [[https://github.com/minad/marginalia][Marginalia]] and [[https://github.com/minad/consult][Consult]] packages. Neither of those packages is a dependency of Embark, but Embark supplies a hook for Consult where Consult previews can be done from Embark Collect buffers: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package embark-consult :after (embark consult) :demand t ; only necessary if you have the hook below @@ -950,7 +933,7 @@ According to [[https://elpa.gnu.org/packages/embark-consult.html#orgc76b5de][thi ;; auto-updating embark collect buffer :hook (embark-collect-mode . consult-preview-at-point-mode)) -#+END_SRC +#+end_src According to the [[https://elpa.gnu.org/packages/embark-consult.html][Embark-Consult page]]: #+begin_quote @@ -958,7 +941,7 @@ Users of the popular [[https://github.com/justbur/emacs-which-key][which-key]] p #+end_quote In other words, typing ~s-;~ to call Embark, specifies the options in a buffer, but the following code puts them in a smaller configuration directly above the selections. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun embark-which-key-indicator () "An embark indicator that displays keymaps using which-key. The which-key help message will show the type and value of the @@ -996,17 +979,17 @@ In other words, typing ~s-;~ to call Embark, specifies the options in a buffer, (advice-add #'embark-completing-read-prompter :around #'embark-hide-which-key-indicator) -#+END_SRC +#+end_src ** Evil Extensions *** Evil Exchange I often use the Emacs commands, ~M-t~ and whatnot to exchange words and whatnot, but this requires a drop out of normal state mode. The [[https://github.com/Dewdrops/evil-exchange][evil-exchange]] project attempts to do something similar, but in a VI-way. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package evil-exchange ;; While I normally just use `link-hint', the gx keybinding is used by evil-exchange: :general (:states 'normal "gz" 'browse-url-at-point) :config (evil-exchange-install)) -#+END_SRC +#+end_src Let’s explain how this works as the documentation assumes some previous knowledge. If you had a sentence: @@ -1020,37 +1003,37 @@ The idea is that you can exchange anything. The ~g x~ marks something (like what *** Evil Commentary The [[https://github.com/linktohack/evil-commentary][evil-commentary]] is a VI-like way of commenting text. Yeah, I typically type ~M-;~ to call Emacs’ originally functionality, but in this case, ~g c c~ comments out a line(s), and ~g c~ takes text objects and whatnot. For instance, ~g c $~ comments to the end of the line. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package evil-commentary :config (evil-commentary-mode)) -#+END_SRC +#+end_src *** Evil Collection Dropping into Emacs state is better than pure Evil state for applications, however, [[https://github.com/emacs-evil/evil-collection][the evil-collection package]] creates a hybrid between the two, that I like. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package evil-collection :after evil :config (evil-collection-init)) -#+END_SRC +#+end_src Do I want to specify the list of modes to change for =evil-collection-init=, e.g. -#+BEGIN_SRC emacs-lisp :tangle no :eval no +#+begin_src emacs-lisp :tangle no :eval no '(eww magit dired notmuch term wdired) -#+END_SRC +#+end_src *** Evil Owl Not sure what is in a register? Have it show you when you hit ~”~ or ~@~ with [[https://github.com/mamapanda/evil-owl][evil-owl]]: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package evil-owl :config (setq evil-owl-display-method 'posframe evil-owl-extra-posframe-args '(:width 50 :height 20 :background-color "#444") evil-owl-max-string-length 50) (evil-owl-mode)) -#+END_SRC +#+end_src *** Evil Snipe Doom introduced me to [[https://github.com/hlissner/evil-snipe][evil-snipe]] which is similar to =f= and =t=, but does two characters, and can, when configured, search more than the current line. My issue is that [[Evil Surround]] uses the same keybindings. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package evil-snipe :after evil :init @@ -1062,12 +1045,12 @@ Doom introduced me to [[https://github.com/hlissner/evil-snipe][evil-snipe]] whi "S" 'evil-snipe-S) :config (evil-snipe-mode +1)) -#+END_SRC +#+end_src It highlights all potential matches, use ~;~ to skip to the next match, and ~,~ to jump back. *** Evil Surround I like both [[https://github.com/emacs-evil/evil-surround][evil-surround]] and Henrik's [[https://github.com/hlissner/evil-snipe][evil-snipe]], however, they both start with ~s~, and conflict, and getting them to work together means I have to remember when does ~s~ call sniper and when it calls surround. As an original Emacs person, I am not bound by that key history, but I do need them consistent, so I’m choosing the ~s~ to be /surround/. -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (use-package evil-surround :after evil-snipe :config @@ -1087,28 +1070,28 @@ I like both [[https://github.com/emacs-evil/evil-surround][evil-surround]] and H "Z" 'evil-Surround-edit) :hook (text-mode . evil-surround-mode)) ; Don't globally use it on Magit, et. al -#+END_SRC +#+end_src Notes: - ~cz'"~ :: to convert surrounding single quote string to double quotes. - ~dz"~ :: to delete the surrounding double quotes. - ~yze"~ :: puts single quotes around the next word. - - ~yziw'~ :: puts single quotes around the word, no matter where the point is positioned. + - ~yziw'~ :: puts single quotes around the word, no matter the points position. - ~yZ$

~ :: surrouds the line with HTML =

= tag (with extra carriage returns). - ~(~ :: puts spaces /inside/ the surrounding parens, but ~)~ doesn't. Same with ~[~ and ~]~. ** Additional Global Packages *** Visual Replace with Visual Regular Expressions -I really appreciated the [[https://github.com/benma/visual-regexp.el][visual-regexp package]] to see what will be changed /before/ executing the replace. -#+BEGIN_SRC emacs-lisp +I appreciated the [[https://github.com/benma/visual-regexp.el][visual-regexp package]] to see what you want to change /before/ executing the replace. +#+begin_src emacs-lisp (use-package visual-regexp :bind (("C-c r" . vr/replace) ("C-c q" . vr/query-replace)) :general (:states 'normal "gR" '("replace" . vr/replace))) -#+END_SRC +#+end_src *** Jump with Avy While I grew up on =Control S=, I am liking the /mental model/ associated with the [[https://github.com/abo-abo/avy][avy project]] that allows a /jump/ among matches across all visible windows. I use the ~F18~ key on my keyboard that should be easy to use, but ~g o~ seems obvious. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package avy :init (setq avy-all-windows t @@ -1123,14 +1106,14 @@ While I grew up on =Control S=, I am liking the /mental model/ associated with t (:states 'normal "go" 'avy-goto-char-timer) :bind ("" . avy-goto-char-timer)) -#+END_SRC +#+end_src *Note:* The links should be shorter near the point as opposed to starting from the top of the window. -Just realized that if you hit the following keys /before/ you select a target, you get a special action: +If you hit the following keys /before/ you select a target, you get a special action: - ~n~ :: copies the matching target word *** Link Hint, the Link Jumper -I originally appreciated [[https://github.com/abo-abo/ace-link][ace-link]] to work with hyperlinks on Org, EWW and Info pages, however, the [[https://github.com/noctuid/link-hint.el][link-hint]] project works with more types of links: -#+BEGIN_SRC emacs-lisp +I originally appreciated [[https://github.com/abo-abo/ace-link][ace-link]] to work with hyperlinks on Org, EWW and Info pages, but the [[https://github.com/noctuid/link-hint.el][link-hint]] project works with more types of links: +#+begin_src emacs-lisp (use-package link-hint :bind ("s-o" . link-hint-open-link) @@ -1144,24 +1127,24 @@ I originally appreciated [[https://github.com/abo-abo/ace-link][ace-link]] to wo "o" 'link-hint-open-link) (:states 'normal :keymaps 'Info-mode-map "o" 'link-hint-open-link)) -#+END_SRC +#+end_src *** Expand Region -Magnar Sveen's [[https://github.com/magnars/expand-region.el][expand-region]] project allows me to hit ~v~ repeatedly, having the selection grow by syntactical units. -#+BEGIN_SRC emacs-lisp +Magnar Sveen's [[https://github.com/magnars/expand-region.el][expand-region]] project allows me to hit ~v~ in =visual= mode, and have the selection grow by syntactical units. +#+begin_src emacs-lisp (use-package expand-region :bind ("C-=" . er/expand-region) :general ;; Use escape to get out of visual mode, but hitting v again expands the selection. (:states 'visual "v" 'er/expand-region)) -#+END_SRC +#+end_src * Working Layout While editing any file on disk is easy enough, I like the mental context switch associated with a full-screen window frame showing all the buffers of a /project task/ (often a direct link to a repository project, but not always). ** Projects -While I really don't /need/ all the features that [[https://github.com/bbatsov/projectile][projectile]] provides, it has all the features I do need, and is easy enough to install. I am referring to the fact that I /could/ use the built-in =project.el= system (see [[https://cestlaz.github.io/post/using-emacs-79-project/][this essay]] for details on what I mean as an alternative). +While I don't /need/ all the features that [[https://github.com/bbatsov/projectile][projectile]] provides, it has all the features I do need, and is easy enough to install. I am referring to the fact that I /could/ use the built-in =project.el= system (see [[https://cestlaz.github.io/post/using-emacs-79-project/][this essay]] for details on what I mean as an alternative). -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package projectile :custom (projectile-sort-order 'recentf) @@ -1190,14 +1173,14 @@ While I really don't /need/ all the features that [[https://github.com/bbatsov/p "p R" '("run project" . projectile-run-project) "p S" '("save project files" . projectile-save-project-buffers) "p T" '("test project" . projectile-test-project))) -#+END_SRC +#+end_src ** Workspaces A /workspace/ (at least to me) requires a quick jump to a collection of buffer windows organized around a project or task. For this, I'm basing my work on the [[https://github.com/nex3/perspective-el][perspective.el]] project. I build a Hydra to dynamically list the current projects as well as select the project. To do this, we need a way to generate a string of the perspectives in alphabetical order: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha--persp-label (num names) "Return string of numbered elements. NUM is the starting number and NAMES is a list of strings." @@ -1212,11 +1195,11 @@ To do this, we need a way to generate a string of the perspectives in alphabetic (defun ha-persp-labels () "Return a string of numbered elements from a list of names." (ha--persp-label 1 (sort (hash-table-keys (perspectives-hash)) 's-less?))) -#+END_SRC +#+end_src Build the hydra as well as configure the =perspective= project. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package perspective :custom (persp-modestring-short t) @@ -1261,25 +1244,25 @@ Build the hydra as well as configure the =perspective= project. ("C-g" nil)) :bind ("C-" . hydra-workspace-leader/body)) -#+END_SRC +#+end_src I have no idea why this binding doesn’t work /within/ the =use-package= declaration, but oh well… -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "TAB" '("workspaces" . hydra-workspace-leader/body)) -#+END_SRC +#+end_src The /special/ perspective is a nice shortcut to the one I use the most: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-switch-to-special () "Change to the projects perspective." (interactive) (persp-switch "projects")) -#+END_SRC +#+end_src *** Predefined Workspaces Let's describe a list of startup project workspaces. This way, I don't need the clutter of the recent state, but also get back to a state of mental normality. Granted, this list is essentially a list of projects that I'm currently developing, so I expect this to change often. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defvar ha-workspace-projects-personal nil "List of default projects with a name.") (add-to-list 'ha-workspace-projects-personal @@ -1290,13 +1273,12 @@ Granted, this list is essentially a list of projects that I'm currently developi '("technical" "~/technical" ("ansible.org"))) (add-to-list 'ha-workspace-projects-personal '("hamacs" "~/other/hamacs" ("README.org" "ha-config.org"))) -#+END_SRC +#+end_src -Given a list of information about project-workspaces, can we just create them all? - -#+BEGIN_SRC emacs-lisp +Given a list of information about project-workspaces, can we create them all? +#+begin_src emacs-lisp (defun ha-persp-exists? (name) - "Return non-nill is a perspective of NAME has been created." + "Return non-nill if a perspective of NAME exists." (when (fboundp 'perspectives-hash) (seq-contains (hash-table-keys (perspectives-hash)) name))) @@ -1315,10 +1297,10 @@ Given a list of information about project-workspaces, can we just create them al (unless (ha-persp-exists? name) (message "Creating workspace: %s (from %s)" name root) (ha-project-persp root name files))))) -#+END_SRC -Often, but not always, I want a perspective based on an actual Git repository, e.g. a project. Projectile keeps state of a "project" based on the current file loaded, so we /combine/ the two projects by first choosing from a list of /known projects/ and then creating a perspective based on the name. To pin the perspective to a project, we just need to load a file from it, e.g. Like a README or something. +#+end_src +Often, but not always, I want a perspective based on an actual Git repository, e.g. a project. Projectile keeps state of a "project" based on the current file loaded, so we /combine/ the two projects by first choosing from a list of /known projects/ and then creating a perspective based on the name. To pin the perspective to a project, we load a file from it, e.g. Like a README or something. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-project-persp (project &optional name files) "Create a new perspective, and then switch to the PROJECT using projectile. If NAME is not given, then figure it out based on the name of the @@ -1345,10 +1327,10 @@ Often, but not always, I want a perspective based on an actual Git repository, e ((f-exists? readme-org) (find-file readme-org)) ((f-exists? readme-md) (find-file readme-md)) (t (dired project)))))) -#+END_SRC -Displaying a few files? Well, when /starting/ I am only showing one or two files (maybe three), so we will split the window horizontally for each file. +#+end_src -#+BEGIN_SRC emacs-lisp +When starting a new perspective, and I specify more than one file, this function splits the window horizontally for each file. +#+begin_src emacs-lisp (defun ha--project-show-files (root files) "Display a list of FILES in a project ROOT directory. Each file gets its own window (so don't make the list of files @@ -1361,23 +1343,23 @@ Displaying a few files? Well, when /starting/ I am only showing one or two files (when more (split-window-horizontally) (ha--project-show-files root more)))) -#+END_SRC +#+end_src The =persp-switch= allows me to select or create a new project, but what if we insisted on a new workspace? -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-new-persp (name) (interactive "sNew Workspace: ") (persp-switch name) (cond ((s-ends-with? "mail" name) (notmuch)) ((s-starts-with? "twit" name) (twit)))) -#+END_SRC +#+end_src Once we create the new perspective workspace, if it matches a particular name, I pretty much know what function I would like to call. * Applications -Can we really call these /applications/? +Can we call these /applications/? ** Magit Can not live without [[https://magit.vc/][Magit]], a Git porcelain for Emacs. I stole the bulk of this work from Doom Emacs. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package magit :config ;; The following code re-instates my General Leader key in Magit. @@ -1417,14 +1399,14 @@ Can not live without [[https://magit.vc/][Magit]], a Git porcelain for Emacs. I "g c c" '("Commit" . magit-commit-create) "g c f" '("Fixup" . magit-commit-fixup) "g c b" '("Branch" . magit-branch-and-checkout))) -#+END_SRC +#+end_src The [[https://github.com/emacsmirror/git-timemachine][git-timemachine]] project is cool: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package git-timemachine :config (ha-leader "g t" '("git timemachine" . git-timemachine))) -#+END_SRC +#+end_src *** Gist Using the [[https://github.com/emacsmirror/gist][gist package]] to write code snippets on [[https://gist.github.com/][Github]] seems like it can be useful, but I'm not sure how often. (use-package gist @@ -1440,18 +1422,18 @@ Using the [[https://github.com/emacsmirror/gist][gist package]] to write code sn "g c g" '("gist" . gist-region-or-buffer) ; Post either the current region, or buffer "g c G" '("private gist" . gist-region-or-buffer-private))) ; create private gist from region or buffer -#+BEGIN_SRC emacs-lisp :tangle no -#+END_SRC +#+begin_src emacs-lisp :tangle no +#+end_src The gist project depends on the [[https://github.com/sigma/gh.el][gh library]]. There seems to be a problem with it. -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (use-package gh :straight (:type git :host :github :repo "sigma/gh.el")) -#+END_SRC +#+end_src *** Forge Let's extend Magit with [[https://github.com/magit/forge][Magit Forge]] for working with Github and Gitlab: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package forge :after magit :config @@ -1473,24 +1455,24 @@ Let's extend Magit with [[https://github.com/magit/forge][Magit Forge]] for work "g c i" '("Issue" . forge-create-issue) "g c p" '("Pull request" . forge-create-pullreq))) -#+END_SRC +#+end_src Every /so often/, pop over to the following URLs and generate a new token where the *Note* is =forge=, and then copy that into the [[file:~/.authinfo.gpg][~/.authinfo.gpg]]: - [[https://gitlab.com/-/profile/personal_access_tokens][Gitlab]] - [[https://github.com/settings/tokens][Github]] and make sure this works: -#+BEGIN_SRC emacs-lisp :tangle no :results replace +#+begin_src emacs-lisp :tangle no :results replace (ghub-request "GET" "/user" nil :forge 'github :host "api.github.com" :username "howardabrams" :auth 'forge) -#+END_SRC +#+end_src *** Pushing is Bad Pushing directly to the upstream branch is /bad form/, as one should create a pull request, etc. To prevent an accidental push, we /double-check/ first: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (define-advice magit-push-current-to-upstream (:before (args) query-yes-or-no) "Prompt for confirmation before permitting a push to upstream." (when-let ((branch (magit-get-current-branch))) @@ -1499,12 +1481,12 @@ Pushing directly to the upstream branch is /bad form/, as one should create a pu (or (magit-get-upstream-branch branch) (magit-get "branch" branch "remote")))) (user-error "Push to upstream aborted by user")))) -#+END_SRC +#+end_src ** Web Browsing *** EWW Web pages look pretty good with EWW, but I'm having difficulty getting it to render a web search from DuckDuck. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package eww :init (setq browse-url-browser-function 'eww-browse-url @@ -1534,11 +1516,10 @@ Web pages look pretty good with EWW, but I'm having difficulty getting it to ren "q" 'bury-buffer) (:states 'normal :keymaps 'eww-buffers-mode-map "q" 'bury-buffer)) -#+END_SRC +#+end_src -This function allows Imenu to offer HTML headings in EWW buffers, which is especially helpful for navigating long, technical documents. - -#+BEGIN_SRC emacs-lisp +This function allows Imenu to offer HTML headings in EWW buffers, helpful for navigating long, technical documents. +#+begin_src emacs-lisp (use-package eww :config (defun unpackaged/imenu-eww-headings () @@ -1561,11 +1542,11 @@ This function allows Imenu to offer HTML headings in EWW buffers, which is espec :hook (eww-mode . (lambda () (setq-local imenu-create-index-function #'unpackaged/imenu-eww-headings)))) -#+END_SRC +#+end_src *** Get Pocket The [[https://github.com/alphapapa/pocket-reader.el][pocket-reader]] project connects to the [[https://getpocket.com/en/][Get Pocket]] service. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package pocket-reader :init (setq org-web-tools-pandoc-sleep-time 1) @@ -1573,7 +1554,7 @@ The [[https://github.com/alphapapa/pocket-reader.el][pocket-reader]] project con (ha-leader "o p" '("get pocket" . pocket-reader)) ;; Instead of jumping into Emacs mode to get the `pocket-mode-map', - ;; we just add the keybindings to the normal mode that makes sense. + ;; we add the keybindings to the normal mode that makes sense. :general (:states 'normal :keymaps 'pocket-reader-mode-map "RET" 'pocket-reader-open-url @@ -1605,19 +1586,19 @@ The [[https://github.com/alphapapa/pocket-reader.el][pocket-reader]] project con "g s" 'pocket-reader-resort "g r" 'pocket-reader-refresh)) -#+END_SRC +#+end_src Use these special keywords when searching: - - =:*=, =:favorite= Return only favorited items. - - =:archive= Return only archived items. - - =:unread= Return only unread items (default). + - =:*=, =:favorite= Return favorited items. + - =:archive= Return archived items. + - =:unread= Return unread items (default). - =:all= Return all items. - - =:COUNT= Return at most /COUNT/ (a number) items. This limit persists until a new search is run. - - =:t:TAG=, =t:TAG= Return items with /TAG/ (only one tag may be searched for, a limitation of the Pocket API). + - =:COUNT= Return at most /COUNT/ (a number) items. This limit persists until you start a new search. + - =:t:TAG=, =t:TAG= Return items with /TAG/ (you can search for one tag at a time, a limitation of the Pocket API). ** Neotree I primarily use [[https://github.com/jaypei/emacs-neotree][Neotree]] when I am screen-sharing my Emacs session with collegues as it shows a /project/ like an IDE. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package neotree :general ; evil-collection forgot a couple: (:states 'normal :keymaps 'neotree-mode-map @@ -1625,11 +1606,11 @@ I primarily use [[https://github.com/jaypei/emacs-neotree][Neotree]] when I am s "SPC" 'neotree-quick-look "RET" 'neotree-enter "H" 'neotree-hidden-file-toggle)) -#+END_SRC +#+end_src ** Annotations Let's try [[https://github.com/bastibe/annotate.el][annotate-mode]], which allows you to drop "notes" and then move to them (yes, serious overlap with bookmarks, which we will return to). -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package annotate :config (ha-leader @@ -1641,35 +1622,39 @@ Let's try [[https://github.com/bastibe/annotate.el][annotate-mode]], which allow "n d" '("delete" . annotate-delete) "n s" '("summary" . annotate-show-annotation-summary) "n j" '("next" . annotate-goto-next-annotation) - "n k" '("prev" . annotate-goto-previous-annotation))) -#+END_SRC + "n k" '("prev" . annotate-goto-previous-annotation) + + ;; If a shift binding isn't set, it defaults to non-shift version + ;; Use SPC N N to jump to the next error: + "n N" '("next error" . flycheck-next-error))) +#+end_src Keep the annotations simple, almost /tag-like/, and then the summary allows you to display them. ** Keepass Use the [[https://github.com/ifosch/keepass-mode][keepass-mode]] to view a /read-only/ version of my Keepass file in Emacs: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package keepass-mode) -#+END_SRC +#+end_src When having your point on a key entry, you can copy fields to kill-ring using: - ~u~ :: URL - ~b~ :: user name - ~c~ :: password ** Demo It -Making demonstrations /within/ Emacs with my [[https://github.com/howardabrams/demo-it][demo-it]] project. While it is on MELPA, I want to use my own cloned version to make sure I can keep debugging aspects of it. Especially since its use of [[https://github.com/takaxp/org-tree-slide][org-tree-slide]] has separated so that I can use [[https://github.com/rlister/org-present][org-present]]. -#+BEGIN_SRC emacs-lisp +Making demonstrations /within/ Emacs with my [[https://github.com/howardabrams/demo-it][demo-it]] project. While on MELPA, I want to use my own cloned version to make sure I can keep debugging it. +#+begin_src emacs-lisp (use-package demo-it :straight (:type git :protocol ssh :host github :repo "howardabrams/demo-it") :commands (demo-it-create demo-it-start)) -#+END_SRC +#+end_src ** PDF Viewing Why not [[https://github.com/politza/pdf-tools][view PDF files]] better? To do this, first install the following on a Mac: -#+BEGIN_SRC sh +#+begin_src sh brew install poppler automake -#+END_SRC -And the equivalent on Linux. Actually, it is better to run [[help:pdf-tools-install][pdf-tools-install]], as it will do the above for the system. +#+end_src +Instead run [[help:pdf-tools-install][pdf-tools-install]], as this command will do the above for the system. -Finally, let’s install the Emacs connection to the =pdfinfo= program: -#+BEGIN_SRC emacs-lisp +Let’s install the Emacs connection to the =pdfinfo= program: +#+begin_src emacs-lisp (use-package pdf-tools :mode ("\\.pdf\\'" . pdf-view-mode) :init @@ -1678,16 +1663,15 @@ Finally, let’s install the Emacs connection to the =pdfinfo= program: (:states 'normal :keymaps 'pdf-view-mode-map "gp" 'pdf-view-goto-page ">" 'doc-view-fit-window-to-page)) -#+END_SRC +#+end_src Make sure the [[help:pdf-info-check-epdfinfo][pdf-info-check-epdfinfo]] function works. * Technical Artifacts :noexport: -Let's provide a name so that the file can be required: - -#+BEGIN_SRC emacs-lisp :exports none +Let's provide a name so we can =require= this file: +#+begin_src emacs-lisp :exports none (provide 'ha-config) ;;; ha-config.el ends here -#+END_SRC +#+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~ diff --git a/ha-display.org b/ha-display.org index 4ab05c7..c814c03 100644 --- a/ha-display.org +++ b/ha-display.org @@ -1,15 +1,14 @@ #+TITLE: Emacs Graphical Display Configuration #+AUTHOR: Howard X. Abrams #+DATE: 2020-09-10 -#+FILETAGS: :emacs: A literate programming file to configure the Emacs UI. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-display --- Emacs UI configuration. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,20 +22,20 @@ A literate programming file to configure the Emacs UI. ;; Using `find-file-at-point', and tangle the file to recreate this one . ;; ;;; Code: -#+END_SRC +#+end_src * Dashboard The [[https://github.com/emacs-dashboard/emacs-dashboard][emacs-dashboard]] project makes a nicer startup screen. It requires [[https://github.com/purcell/page-break-lines][page-break-lines]] (which is a nice project): -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package page-break-lines) -#+END_SRC +#+end_src And let’s make this Emacs look more like a fancy IDE with [[https://github.com/domtronn/all-the-icons.el][all-the-icons]]: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package all-the-icons :if (display-graphic-p)) -#+END_SRC +#+end_src -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package dashboard :init (defun ha-dashboard-version () @@ -61,10 +60,10 @@ And let’s make this Emacs look more like a fancy IDE with [[https://github.com (dashboard-setup-startup-hook) (setq dashboard-footer-messages (list (ha--dad-joke)))) -#+END_SRC +#+end_src I would appreciate seeing if my Emacs installation has the features that I expect: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-hamacs-features (&optional iconic) "Simple display of features I'm most keen about. If ICONIC is non-nil, return a string of icons." @@ -86,10 +85,10 @@ I would appreciate seeing if my Emacs installation has the features that I expec (if (called-interactively-p) (message "Enabled features: %s" results) results))) -#+END_SRC +#+end_src * Mode Line Let's install and load some of packages from the [[https://github.com/hlissner/doom-emacs][Doom Emacs]] project, like [[https://github.com/seagle0128/doom-modeline][doom-modeline]] and maybe the themes: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package doom-modeline :init (setq doom-modeline-minor-modes nil @@ -99,11 +98,11 @@ Let's install and load some of packages from the [[https://github.com/hlissner/d (doom-modeline-mode +1)) (use-package doom-themes) -#+END_SRC +#+end_src * Find the Bloody Cursor Large screen, lots of windows, so where is the cursor? While I used to use =hl-line+=, I found that the prolific [[https://protesilaos.com/][Protesilaos Stavrou]] [[https://protesilaos.com/codelog/2022-03-14-emacs-pulsar-demo/][introduced his Pulsar project]] is just what I need. Specifically, I might /loose the cursor/ and need to have it highlighted (using ~F6~), but also, this automatically highlights the cursor line with specific /actions/ , like changing windows. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package pulsar :straight (:type git :protocol ssh :host gitlab :repo "protesilaos/pulsar") :custom @@ -152,52 +151,52 @@ Large screen, lots of windows, so where is the cursor? While I used to use =hl-l (pulsar-face 'pulsar-magenta) (pulsar-delay 0.055) :bind ("" . pulsar-pulse-line)) -#+END_SRC +#+end_src * Themes One does get used to a particular collection of colors. Mine is Tomorrow: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package color-theme-sanityinc-tomorrow) -#+END_SRC +#+end_src Most of the time, Emacs is on my desk is a darkened room, so I choose the dark theme: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun laptop-inside () (interactive) (load-theme 'sanityinc-tomorrow-night t) (set-face-attribute 'region nil :background "#000096") (set-face-attribute 'mode-line nil :background "black") (set-face-attribute 'mode-line-inactive nil :background "#333333")) -#+END_SRC +#+end_src But, when feeling adventurous, I /sometimes/ take my laptop outside: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun laptop-in-the-sun () (interactive) (load-theme 'sanityinc-tomorrow-day t) (set-face-attribute 'region nil :background "orange1") (set-face-attribute 'mode-line nil :background "#cccccc") (set-face-attribute 'mode-line-inactive nil :background "#888888")) -#+END_SRC +#+end_src Oh, and turn off the line highlighting: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (global-hl-line-mode -1) -#+END_SRC +#+end_src And of course, the default is /inside/ where it is dark and safe: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (laptop-inside) -#+END_SRC +#+end_src * Full Size Frame Taken from [[https://emacsredux.com/blog/2020/12/04/maximize-the-emacs-frame-on-startup/][this essay]], I figured I would start the initial frame automatically in fullscreen, but not any subsequent frames (as this could be part of the capturing system). -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (add-to-list 'initial-frame-alist '(fullscreen . maximized)) -#+END_SRC +#+end_src * Font Configuration Am I ever really ever satisfied with any font? I regularly change my font based on the monospace du jour... [[http://blogs.adobe.com/typblography/2012/09/source-code-pro.html][Source Code Pro]] is attractive, and has been a staple on every programmers' screen. However, we all want ligatures, [[https://github.com/i-tu/Hasklig][Hasklig]] is a nice font that is thinner and easier to read than [[https://github.com/tonsky/FiraCode][Fira]], but [[https://typeof.net/Iosevka/][Iosevka]] seems to have it all. Oh, Microsoft just gave us [[https://docs.microsoft.com/en-us/windows/terminal/cascadia-code][Cascadia]] and that seems shiny. However, the [[https://github.com/ryanoasis/nerd-fonts][Nerd Font project]] adds the ligatures as well as all the other niceties to a font. @@ -226,7 +225,7 @@ I stole the following idea from [[https://protesilaos.com/dotemacs/#h:9035a1ed-e The following is from [[https://source-foundry.github.io/Hack/font-specimen.html][Hack's website]]: -#+BEGIN_SRC c +#+begin_src c // The four boxing wizards jump #include // <= quickly. int main(int argc, char **argv) { @@ -236,16 +235,16 @@ int main(int argc, char **argv) { printf("@$Hamburgefo%c`",'\n'); return ~7&8^9?0:l1|!"j->k+=*w"; } -#+END_SRC +#+end_src To install a font, I use the following command on my Mac: -#+BEGIN_SRC sh +#+begin_src sh brew tap homebrew/cask-fonts brew install --cask font-hack-nerd-font -#+END_SRC +#+end_src ** Specifying a Font My /current/ favorite font is actually the top list of fonts that may be installed on my system (they usually are): -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defvar ha-fixed-font (when window-system (cond @@ -261,15 +260,15 @@ My /current/ favorite font is actually the top list of fonts that may be install ((x-list-fonts "Anonymous Pro") "Anonymous Pro") (t "monospaced"))) "My fixed width font based on what is installed, `nil' if not defined.") -#+END_SRC +#+end_src Force something as well: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (setq ha-fixed-font "Hack Nerd Font") -#+END_SRC +#+end_src I probably don't need to have such a ranking system, as chances are really good that I'll have all of them installed. Still. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defvar ha-variable-font (when window-system (cond ((x-list-fonts "Overpass") "Overpass") @@ -279,22 +278,22 @@ I probably don't need to have such a ranking system, as chances are really good ((x-family-fonts "Sans Serif") "Sans Serif") (nil (warn "Cannot find a Sans Serif Font. Install Source Sans Pro.")))) "My variable width font available to org-mode files and whatnot.") -#+END_SRC +#+end_src Simple function that gives me the font information based on the size I need. Recently updated after reading [[https://protesilaos.com/codelog/2020-09-05-emacs-note-mixed-font-heights/][this essay]], as I wanted my =fixed-pitch= to scale along with my =variable-pitch= font. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-set-favorite-font-size (size) "Set the default font size as well as equalize the fixed and variable fonts." (let ((fav-font (format "%s-%d" ha-fixed-font size))) (set-face-attribute 'default nil :font fav-font) (set-face-attribute 'fixed-pitch nil :family ha-fixed-font :inherit 'default :height 1.0) (set-face-attribute 'variable-pitch nil :family ha-variable-font :inherit 'default :height 1.2))) -#+END_SRC +#+end_src Define /interactive/ functions to quickly adjusting the font size based on my computing scenario: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-mac-monitor-fontsize () "Quickly set reset my font size when I connect my laptop to a monitor on a Mac." (interactive) @@ -319,11 +318,11 @@ Define /interactive/ functions to quickly adjusting the font size based on my co "Quickly set reset my font size when I am on my iMac." (interactive) (ha-set-favorite-font-size 16)) -#+END_SRC +#+end_src Which font to choose? -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun font-monitor-size-default () "Set the default size according to my preference." (interactive) @@ -340,12 +339,12 @@ Which font to choose? (ha-mac-laptop-fontsize))) (font-monitor-size-default) -#+END_SRC +#+end_src ** Zooming or Increasing Font Size Do we want to increase the size of font in a single window (using =text-scale-increase=), or globally (using my new =font-size-increase=)? Increase or decrease the set size of the face: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun font-size-adjust (delta) "Adjust the current frame's font size. DELTA would be something like 1 or -1." @@ -366,25 +365,25 @@ Increase or decrease the set size of the face: "Decrease the `default' font size of all frames." (interactive) (font-size-adjust -1)) -#+END_SRC +#+end_src And some keybindings to call them: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (global-set-key (kbd "s-+") 'font-size-increase) (global-set-key (kbd "s-=") 'font-size-increase) (global-set-key (kbd "s--") 'font-size-decrease) (global-set-key (kbd "s-0") 'font-size-monitor-default) (global-set-key (kbd "s-9") 'font-size-laptop-default) -#+END_SRC +#+end_src * Emojis, Icons and Whatnot -Sometimes two symbols should really be one, for instance: -#+BEGIN_SRC emacs-lisp +Display these two symbols as one: +#+begin_src emacs-lisp (add-hook 'text-mode-hook (lambda () (push '("!?" . "‽") prettify-symbols-alist))) -#+END_SRC +#+end_src -In Emacs 28.1, we have better Unicode 14 support. Which means, we need to install [[https://github.com/googlefonts/noto-emoji][Noto Color Emoji]]. Currently, my systems, seems to work just fine, but I’m leaving this code here in case I have issues, as I might use what Apple supplies when on a Mac (thanks [[http://xahlee.info/emacs/emacs/emacs_list_and_set_font.html][Xah Lee]]): -#+BEGIN_SRC emacs-lisp :tangle no +In Emacs 28.1, we have better Unicode 14 support. Which means, we need to install [[https://github.com/googlefonts/noto-emoji][Noto Color Emoji]]. My systems, seems to work fine, but I’m leaving this code here in case I have issues, as I might use what Apple supplies when on a Mac (thanks [[http://xahlee.info/emacs/emacs/emacs_list_and_set_font.html][Xah Lee]]): +#+begin_src emacs-lisp :tangle no ;; set font for symbols (set-fontset-font t 'symbol (cond @@ -401,36 +400,35 @@ In Emacs 28.1, we have better Unicode 14 support. Which means, we need to instal ((member "Apple Color Emoji" (font-family-list)) "Apple Color Emoji") ((member "Noto Color Emoji" (font-family-list)) "Noto Color Emoji") ((member "Symbola" (font-family-list)) "Symbola"))) -#+END_SRC +#+end_src Test this out: 😄 😱 😸 👸 👽 🙋 Not use what I'm doing with the [[https://github.com/domtronn/all-the-icons.el][all-the-icons]] package, but the Doom Modeline uses much of this. -#+BEGIN_SRC emacs-lisp -(use-package all-the-icons) -#+END_SRC +#+begin_src emacs-lisp + (use-package all-the-icons) +#+end_src *Note:* Install everything with the function, =all-the-icons-install-fonts=. * Ligatures Seems like getting ligatures to work in Emacs has been a Holy Grail. On Mac, I've used special builds that have hacks, but now with Emacs 27 and Harfbuzz, I should be able to get --> to look like it should. -#+BEGIN_SRC emacs-lisp :tangle no -(setq prettify-symbols-unprettify-at-point 'right-edge) +#+begin_src emacs-lisp :tangle no + (setq prettify-symbols-unprettify-at-point 'right-edge) -(global-prettify-symbols-mode +1) -(prettify-symbols-mode +1) -#+END_SRC + (global-prettify-symbols-mode +1) + (prettify-symbols-mode +1) +#+end_src Note, in Doom, is appears we have a =ligatures= module. We'll start using that instead, but changing it in [[file:general-programming.org][general-programming]] file. * Technical Artifacts :noexport: -Let's provide a name so that the file can be required: - -#+BEGIN_SRC emacs-lisp :exports none -(provide 'ha-display) -;;; ha-display.el ends here -#+END_SRC +Let's =provide= a name so we can =require= this file: +#+begin_src emacs-lisp :exports none + (provide 'ha-display) + ;;; ha-display.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~ diff --git a/ha-email.org b/ha-email.org index e15a479..ee5c51b 100644 --- a/ha-email.org +++ b/ha-email.org @@ -1,15 +1,14 @@ #+TITLE: Configuring Emacs for Email with Notmuch #+AUTHOR: Howard X. Abrams #+DATE: 2020-09-16 -#+FILETAGS: :emacs: A literate configuration file for email using Notmuch. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-email --- Email configuration using Notmuch. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,36 +22,36 @@ A literate configuration file for email using Notmuch. ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC +#+end_src * Introduction -To use this system, begin with ~SPC a m~ (after a ~SPC a n~ to asychronously download new mail ... which probably should be running regularly). +To use this system, begin with ~SPC a m~ (after a ~SPC a n~ to asychronously download new mail … which probably should be running beforehand). When the Notmuch interface up, hit ~J~ to jump to one of the Search boxes (described below). Typically, this is ~i~ for the Imbox, check out the focused message from people I care). Hit ~q~ to return. -Next type ~s~ to view and organize mail I've never seen before. We need to keep things focused, so regularly /making auto filtering rules/ is important. Move the point to the message and hit one of the following to automatically move the sender to a pre-defined box: +Next type ~s~ to view and organize mail I've never seen before. We need to focus the display, so /creating auto filtering rules/ is important. Move the point to the message and hit one of the following to automatically move the sender to a pre-defined box: - - ~I~ :: screened stuff that important enough to go to my Imbox - - ~S~ :: spam ... so much goes here + - ~I~ :: screened email important enough to go to my Imbox + - ~S~ :: spam … so much goes here - ~P~ :: receipts go to this *Paper Trail*, which takes a *tag* as the name of the store - - ~f~ :: mailing lists and other things that might be nice to read go to *The Feed* + - ~f~ :: mailing lists and other email that might be nice to read go to *The Feed* ** Email Addresses -The configuration files below expect email addresses (I store passwords and other encrypted information elsewhere). These email addresses are /hardly/ private, but I figured I would annoy any screenscraping spam-inducing crawlers out there, while still allowing others to follow my lead on configuring Emacs and Email. +The configuration files below expect email addresses (I store passwords and other encrypted information elsewhere). These email addresses are /not/ private, but I figured I would annoy any screenscraping spam-inducing crawlers out there, while still allowing others to follow my lead on configuring Emacs and Email. #+NAME: email-address-1 -#+BEGIN_SRC emacs-lisp :exports none :tangle no :results silent +#+begin_src emacs-lisp :exports none :tangle no :results silent (rot13-string "ubjneq.noenzf@tznvy.pbz") -#+END_SRC +#+end_src #+NAME: email-address-2 -#+BEGIN_SRC emacs-lisp :exports none :tangle no :results silent +#+begin_src emacs-lisp :exports none :tangle no :results silent (rot13-string "ubjneq@ubjneqnoenzf.pbz") -#+END_SRC +#+end_src #+NAME: email-address-3 -#+BEGIN_SRC emacs-lisp :exports none :tangle no :results silent +#+begin_src emacs-lisp :exports none :tangle no :results silent (rot13-string "ubjneq@shmmlgbnfg.pbz") -#+END_SRC +#+end_src To use these, we set the =:noweb yes= (to pull in the /name/ of the code block) but put a pair of parens after the name to have it evaluated. For instance: #+begin_example @@ -65,19 +64,19 @@ some-address: <> To begin, we need the code. On Ubuntu, this is: -#+BEGIN_SRC shell :tangle no +#+begin_src shell :tangle no sudo apt install -y notmuch -#+END_SRC +#+end_src And on MacOS, we use =brew=: -#+BEGIN_SRC shell :tangle no +#+begin_src shell :tangle no brew install notmuch -#+END_SRC +#+end_src Next, we need some basic configuration settings and some global keybindings: -#+BEGIN_SRC emacs-lisp :noweb yes +#+begin_src emacs-lisp :noweb yes (use-package notmuch :init (setq mail-user-agent 'notmuch-user-agent @@ -109,26 +108,26 @@ Next, we need some basic configuration settings and some global keybindings: "C" '("reply-later" . hey-notmuch-reply-later)) <> <>) -#+END_SRC +#+end_src Also, let's do some basic configuration of Emacs' mail system: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq mm-text-html-renderer 'shr mail-specify-envelope-from t message-kill-buffer-on-exit t message-send-mail-function 'message-send-mail-with-sendmail message-sendmail-envelope-from 'header) -#+END_SRC +#+end_src * Configuration Do I want to sign messages by default? Nope. -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (add-hook 'message-setup-hook 'mml-secure-sign-pgpmime) -#+END_SRC +#+end_src ** Addresses I need to incorporate an address book again, but in the meantime, searching through a history of past email works well enough. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq notmuch-address-selection-function (lambda (prompt collection initial-input) (completing-read prompt @@ -137,7 +136,7 @@ I need to incorporate an address book again, but in the meantime, searching thro t nil 'notmuch-address-history))) -#+END_SRC +#+end_src ** Sending Messages Do I need to set up [[https://marlam.de/msmtp/][MSMTP]]? No, as Notmuch will do that work. @@ -147,19 +146,18 @@ To do this, type ~c~ and select an option (including ~r~ to reply). When we start notmuch, we need to retrieve the email and then process it. Most of this is actually contained in the Notmuch configuration. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun notmuch-retrieve-messages () "Retrieve and process my mail messages." (interactive) (async-shell-command "notmuch new")) -#+END_SRC +#+end_src * iSync Configuration -Using [[https://isync.sourceforge.io/][isync]] (or is it =mbsync=) for mail retrieval. -Currently, I have a couple of Google Mail accounts that I want connected. +Using [[https://isync.sourceforge.io/][isync]] (or is it =mbsync=) for mail retrieval. I have a couple of Google Mail accounts that I want connected. -The file generally can have a =Pass= entry for the encrypted passcode, but in order to demonstrate how to connect to multiple accounts, I'm using a GPG daemon: +The file generally can have a =Pass= entry for the encrypted passcode, but to show how to connect to more than one accounts, I'm using a GPG daemon: -#+BEGIN_SRC conf :tangle ~/.mbsyncrc :noweb yes +#+begin_src conf :tangle ~/.mbsyncrc :noweb yes # Note: We now tangle this file from ~/other/hamacs/ha-email.org Create Both SyncState * @@ -169,7 +167,7 @@ The file generally can have a =Pass= entry for the encrypted passcode, but in or # PERSONAL ACCOUNT IMAPAccount personal Host imap.gmail.com - User <> # Obviously, you'd substitute your own email address here + User <> # Substitute your own email address here PassCmd "gpg --quiet --for-your-eyes-only --no-tty --decrypt ~/.mailpass-personal.gpg" SSLType IMAPS AuthMechs LOGIN @@ -232,67 +230,63 @@ The file generally can have a =Pass= entry for the encrypted passcode, but in or Master :gmail-remote:"[Gmail]/Trash" Slave :gmail-local:trash ExpireUnread yes -#+END_SRC +#+end_src * Notmuch Configuration -Notmuch requires a few configuration files. +Notmuch requires these configuration files. ** =notmuch-config= The general settings file that goes into =~/.notmuch-config=: -#+BEGIN_SRC conf-unix :tangle ~/.notmuch-config +#+begin_src conf-unix :tangle ~/.notmuch-config # .notmuch-config - Configuration file for the notmuch mail system # Note: We now tangle this file from ~/other/hamacs/ha-email.org # # For more information about notmuch, see https://notmuchmail.org -#+END_SRC +#+end_src The commentary for each of the subsections came from their man page. *** Database configuration -The only value supported here is 'path' which should be the top-level directory where your mail currently exists and to where mail will be delivered in the future. Files should be individual email messages. Notmuch will store its database within a sub-directory of the path configured here named ".notmuch". +The value supported here is =path= which should be the top-level directory where your mail exists and to where =mbsync= will new mail. Files should be individual email messages. Notmuch will store its database within a sub-directory of the path configured here named ".notmuch". -#+BEGIN_SRC conf-unix :tangle ~/.notmuch-config +#+begin_src conf-unix :tangle ~/.notmuch-config [database] path=.mail -#+END_SRC +#+end_src *** User configuration -Here is where you can let notmuch know how you would like to be addressed. Valid settings are - +Here is where you can let notmuch know how you address emails. Valid settings are - =name= :: Your full name. - =primary_email= :: Your primary email address. - =other_email= :: A list (separated by =;=) of other email addresses at which you receive email. -Notmuch will use the various email addresses configured here when formatting replies. It will avoid including your own addresses in the recipient list of replies, and will set the From address based on the address to which the original email was addressed. +Notmuch use the email addresses configured here when formatting replies. It will avoid including your own addresses in the recipient list of replies, and will set the From address based on the address in the original email. -#+BEGIN_SRC conf-unix :tangle ~/.notmuch-config :noweb yes +#+begin_src conf-unix :tangle ~/.notmuch-config :noweb yes [user] name=Howard Abrams primary_email=<> other_email=<>;<> -#+END_SRC -*NB:* In the configuration above, you may see the addresses are all set to =nil=. If you are copying this from a rendered web page, just note that you need to substitute that with your own email address. +#+end_src +*NB:* In the configuration above, you may see the addresses are all set to =nil=. If you are copying this from a rendered web page, note that you need to substitute that with your own email address. *** Configuration for "notmuch new" -The following options are supported here: - - - =tags= :: A list (separated by =;=) of the tags that will be added to all messages incorporated by "notmuch new". - +Note the following supported options: + - =tags= :: A list (separated by =;=) of the tags that added to all messages incorporated by "notmuch new". - =ignore= :: A list (separated by =;=) of file and directory names that will not be searched for messages by "notmuch new". NOTE: *Every* file/directory that goes by one of those names will be ignored, independent of its depth/location in the mail store. - -#+BEGIN_SRC conf-unix :tangle ~/.notmuch-config +#+begin_src conf-unix :tangle ~/.notmuch-config [new] tags=unread;inbox; ignore= -#+END_SRC +#+end_src *** Search configuration The following option is supported here: - =exclude_tags= :: A ;-separated list of tags that will be excluded from search results by default. Using an excluded tag in a query will override that exclusion. -#+BEGIN_SRC conf-unix :tangle ~/.notmuch-config +#+begin_src conf-unix :tangle ~/.notmuch-config [search] exclude_tags=deleted;spam; -#+END_SRC +#+end_src *** Maildir compatibility configuration The following option is supported here: @@ -308,16 +302,16 @@ The following option is supported here: The =notmuch new= command will notice flag changes in filenames and update tags, while the =notmuch tag= and =notmuch restore= commands will notice tag changes and update flags in filenames. -#+BEGIN_SRC conf-unix :tangle ~/.notmuch-config +#+begin_src conf-unix :tangle ~/.notmuch-config [maildir] synchronize_flags=true -#+END_SRC +#+end_src That should complete the Notmuch configuration. ** =pre-new= Then we need a shell script called when beginning a retrieval, =pre-new= that simply calls =mbsync= to download all the messages: -#+BEGIN_SRC shell :tangle ~/.mail/.notmuch/hooks/pre-new :shebang "#!/bin/bash" +#+begin_src shell :tangle ~/.mail/.notmuch/hooks/pre-new :shebang "#!/bin/bash" # More info about hooks: https://notmuchmail.org/manpages/notmuch-hooks-5/ # Note: We now tangle this file from ~/other/hamacs/ha-email.org @@ -326,11 +320,11 @@ echo "Starting not-much 'pre-new' script" mbsync -a echo "Completing not-much 'pre-new' script" -#+END_SRC +#+end_src ** =post-new= And a =post-new= hook based on a filtering scheme that mimics the Hey.com workflow taken from [[https://gist.githubusercontent.com/frozencemetery/5042526/raw/57195ba748e336de80c27519fe66e428e5003ab8/post-new][this gist]] (note we have more to say on that later on) to filter and tag all messages after they have arrived: -#+BEGIN_SRC shell :tangle ~/.mail/.notmuch/hooks/post-new :shebang "#!/bin/bash" +#+begin_src shell :tangle ~/.mail/.notmuch/hooks/post-new :shebang "#!/bin/bash" # Based On: https://gist.githubusercontent.com/frozencemetery/5042526/raw/57195ba748e336de80c27519fe66e428e5003ab8/post-new # Note: We now tangle this file from ~/other/hamacs/ha-email.org # @@ -422,7 +416,7 @@ timer_end "Old-Projects" notmuch tag +screened 'subject:[Web]' echo "Completing not-much 'post-new' script" -#+END_SRC +#+end_src * Hey I originally took the following configuration from [[https://youtu.be/wuSPssykPtE][Vedang Manerikar's video]], along with [[https://gist.github.com/vedang/26a94c459c46e45bc3a9ec935457c80f][the code]]. The ideas brought out were to mimic the hey.com email workflow, and while not bad, I thought that maybe I could improve upon it slowly over time. @@ -431,7 +425,7 @@ To allow me to keep Vedang's and my code side-by-side in the same Emacs variable A list of pre-defined searches act like "Folder buttons" at the top to quickly see files that match those /buckets/: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq notmuch-saved-searches '((:name "Imbox" :query "tag:inbox AND tag:screened AND tag:unread" :key "i" @@ -464,12 +458,12 @@ A list of pre-defined searches act like "Folder buttons" at the top to quickly s (:name "Old Projects" :query "tag:old-project AND NOT tag:unread" :key "X"))) -#+END_SRC +#+end_src ** Helper Functions With good bucket definitions, we should be able to scan the mail quickly and deal with the entire lot of them: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun hey-notmuch-archive-all () "Archive all the emails in the current view." (interactive) @@ -495,11 +489,11 @@ When called directly, BEG and END provide the region." (interactive (notmuch-search-interactive-tag-changes)) (notmuch-search-tag tag-changes beg end) (notmuch-search-archive-thread nil beg end)) -#+END_SRC +#+end_src A key point in organizing emails with the Hey model, is looking at the "from" address: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun hey-notmuch-search-find-from () "A helper function to find the email address for the given email." (let ((notmuch-addr-sexp (first @@ -509,11 +503,11 @@ A key point in organizing emails with the Hey model, is looking at the "from" ad "--output=sender" (notmuch-search-find-thread-id))))) (plist-get notmuch-addr-sexp :address))) -#+END_SRC +#+end_src And we can create a filter, /search/ and tagging based on this "from" function: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun hey-notmuch-filter-by-from () "Filter the current search view to show all emails sent from the sender of the current thread." (interactive) @@ -546,13 +540,13 @@ search." (when refresh (set-buffer this-buf) (notmuch-refresh-this-buffer)))) -#+END_SRC +#+end_src ** Moving Mail to Buckets We based the Hey buckets on notmuch databases, we combine the =hey-notmuch-add-addr-to-db= with the =hey-notmuch-tag-by-from= functions to move messages. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun hey-notmuch-add-addr-to-db (nmaddr nmdbfile) "Add the email address NMADDR to the db-file NMDBFILE." (append-to-file (format "%s\n" nmaddr) nil nmdbfile)) @@ -626,12 +620,12 @@ This means: (substring (notmuch-show-get-from) 0 15))) (email-string (format "%s (From: %s)" email-subject email-from))) (message "Noted! Reply Later: %s" email-string))) -#+END_SRC +#+end_src ** Bucket Keybindings In /Emacs/ mode, we can just call =define-key=, but since it starts in Evil state (and we may want to use Evil keybindings, let's create some local leaders: #+NAME: local-leader-keybindings -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (general-create-definer ha-mail-hello-leader :states '(normal visual motion) :keymaps 'notmuch-hello-mode-map @@ -652,23 +646,23 @@ In /Emacs/ mode, we can just call =define-key=, but since it starts in Evil stat :prefix "SPC m" :global-prefix "" :non-normal-prefix "S-SPC") -#+END_SRC +#+end_src A series of keybindings to quickly send messages to one of the pre-defined buckets. #+NAME: hey-show-keybindings -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (ha-mail-show-leader "c" '("compose" . notmuch-mua-new-mail) "C" '("reply-later" . hey-notmuch-reply-later)) (define-key notmuch-show-mode-map (kbd "C") 'hey-notmuch-reply-later) -#+END_SRC +#+end_src The bindings in =notmuch-search-mode= are available when looking at a list of messages: #+NAME: hey-search-keybindings -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (ha-mail-search-leader "r" '("reply" . notmuch-search-reply-to-thread) "R" '("reply-all" . notmuch-search-reply-to-thread-sender) @@ -700,19 +694,19 @@ The bindings in =notmuch-search-mode= are available when looking at a list of me (define-key notmuch-search-mode-map (kbd "P") 'hey-notmuch-move-sender-to-papertrail) (define-key notmuch-search-mode-map (kbd "f") 'hey-notmuch-move-sender-to-thefeed) (define-key notmuch-search-mode-map (kbd "C") 'hey-notmuch-reply-later) -#+END_SRC +#+end_src ** Org Integration The gods ordained that Mail and Org should dance together, so step one is composing mail with org: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package org-mime :config (ha-local-leader :keymaps 'notmuch-message-mode-map "s" '("send" . notmuch-mua-send-and-exit) "m" '("mime it" . org-mime-htmlize))) -#+END_SRC +#+end_src A new option is to use [[https://github.com/jeremy-compostella/org-msg][org-msg]], so let's try it: -#+BEGIN_SRC emacs-lisp :noweb yes +#+begin_src emacs-lisp :noweb yes (use-package org-msg :init (setq org-msg-options "html-postamble:nil H:5 num:nil ^:{} toc:nil author:nil email:nil \\n:t" @@ -733,30 +727,29 @@ A new option is to use [[https://github.com/jeremy-compostella/org-msg][org-msg] ,*Howard* /One Emacs to rule them all/ ,#+end_signature")) -#+END_SRC +#+end_src The idea of linking org documents to email could be nice, however, the =ol-notmuch= package in the [[https://elpa.nongnu.org/nongnu/org-contrib.html][org-contrib]] package needs a maintainer. -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (use-package ol-notmuch :after org :straight (:type built-in) :config (add-to-list 'org-modules 'ol-notmuch)) -#+END_SRC +#+end_src To use, read a message and save a link to it with ~SPC o l~. Next, in an org document, create a link with ~SPC m l~. Now, you can return to the message from that document with ~SPC m o~. Regardless, I may need to store a local copy when I upgrade Org. * Display Configuration Using the [[https://github.com/seagle0128/doom-modeline][Doom Modeline]] to add notifications: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package doom-modeline :config (setq doom-modeline-mu4e t)) -#+END_SRC +#+end_src * Technical Artifacts :noexport: -Let's provide a name so that the file can be required: - -#+BEGIN_SRC emacs-lisp :exports none +Let's =provide= a name so we can =require= this file: +#+begin_src emacs-lisp :exports none (provide 'ha-email) ;;; ha-email.el ends here -#+END_SRC +#+end_src #+DESCRIPTION: A literate configuration file for email using Notmuch. diff --git a/ha-feed-reader.org b/ha-feed-reader.org index 77f8e3d..b59d25d 100644 --- a/ha-feed-reader.org +++ b/ha-feed-reader.org @@ -1,15 +1,14 @@ #+TITLE: My RSS Feeds #+AUTHOR: Howard Abrams #+DATE: 2018-08-08 August -#+FILETAGS: :elfeed: A literate programming file for configuring =elfeed= in Emacs. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-config --- ElFeed configuration. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,19 +22,19 @@ A literate programming file for configuring =elfeed= in Emacs. ;; Using `find-file-at-point', and tangle the file to recreate this one . ;; ;;; Code: -#+END_SRC +#+end_src * Configuring Elfeed Let's get our feeds from a collection of org mode files. By default, Doom configures =rmh-elfeed-org-files= to [[file:~/Dropbox/org/elfeed.org][elfeed.org]] in =org-directory=, so that will be fine. By setting this variable, we configure elfeed to use elfeed: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq rmh-elfeed-org-files (list (f-join hamacs-source-dir "ha-feed-reader.org"))) -#+END_SRC +#+end_src While I would like to share the /status/ of my reads, so ... -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package elfeed :init (setq elfeed-db-directory "~/dropbox/.elfeed/") @@ -63,11 +62,11 @@ While I would like to share the /status/ of my reads, so ... (interactive) (elfeed-search-untag-all 'unread) (elfeed-search-update)) -#+END_SRC +#+end_src According to Ben Maughan and [[http://pragmaticemacs.com/emacs/to-eww-or-not-to-eww/][this Pragmatic Emacs essay]], we could easily browse an article in the GUI browser instead of EWW with capital B: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package elfeed :config (defun elfeed-show-visit-gui () @@ -78,10 +77,10 @@ According to Ben Maughan and [[http://pragmaticemacs.com/emacs/to-eww-or-not-to- :bind (:map elfeed-show-mode-map ("B" . elfeed-show-visit-gui))) -#+END_SRC +#+end_src Quick way to start and jump to my world of feeds. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-elfeed-persp-start () "Create an ELFEED workspace and start my ELFEED client." (interactive) @@ -92,15 +91,15 @@ Quick way to start and jump to my world of feeds. "Switch to the ELFEED workspace and load the next buffer." (interactive) (persp-switch "feeds")) -#+END_SRC +#+end_src And some global keys to display them in the =apps= menu: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "a f" '("elfeed switch" . ha-elfeed-persp-switch) "a F" '("elfeed start" . ha-elfeed-persp-start)) -#+END_SRC +#+end_src * The Feeds :elfeed: ** Personal :personal: *** [[http://www.howardism.org/index.xml][Howardisms]] :mustread: @@ -250,10 +249,10 @@ Has some good, thought-provoking essays. * Technical Artifacts :noexport: Let's /provide/ a name so we can =require= the file: -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none (provide 'ha-feed-reader) ;;; ha-feed-reader.el ends here -#+END_SRC +#+end_src #+DESCRIPTION: A literate programming file for configuring elfeed. diff --git a/ha-irc.org b/ha-irc.org index b096185..4992252 100644 --- a/ha-irc.org +++ b/ha-irc.org @@ -1,15 +1,14 @@ #+TITLE: IRC and Bitlbee Interface #+AUTHOR: Howard X. Abrams #+DATE: 2020-12-10 -#+FILETAGS: :emacs: A literate programming configuration file for IRC communiction. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-irc.el --- configuration for IRC communication. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,11 +22,11 @@ A literate programming configuration file for IRC communiction. ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC +#+end_src * Introduction My IRC /needs/ are basic, but I'm not sure which I want to use. ** ERC -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package erc-hl-nicks :after erc) @@ -71,20 +70,20 @@ My IRC /needs/ are basic, but I'm not sure which I want to use. (erc :server "howardabrams.com" :port 7777) (sit-for 2) (erc-cmd-QUOTE (format "PASS %s:%s" username password)))) -#+END_SRC +#+end_src I like to make sure the text formats to the size of the window: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-erc-resize-text () "Resize the ERC text to fill the current window." (interactive) (setq erc-fill-column (1- (window-width)))) -#+END_SRC +#+end_src * ZNC Server I'm using my own ZNC server, and I need to log in with a password stored in my GPG auth storage: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-erc-reconnect-password () "Send the reconnection password string." (interactive) @@ -94,11 +93,11 @@ I'm using my own ZNC server, and I need to log in with a password stored in my G (username (plist-get auth-first :user)) (password (funcall (plist-get auth-first :secret)))) (erc-cmd-QUOTE (format "PASS %s:%s" username password)))) -#+END_SRC +#+end_src * Key Bindings Quick way to start and jump to my IRC world. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-irc-persp-start () "Create an IRC workspace and start my IRC client." (interactive) @@ -110,46 +109,46 @@ Quick way to start and jump to my IRC world. (interactive) (persp-switch "irc") (call-interactively 'erc-track-switch-buffer)) -#+END_SRC +#+end_src And some global keys to display them: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "a i" '("irc switch" . ha-irc-persp-switch) "a I" '("irc start" . ha-irc-persp-start)) -#+END_SRC +#+end_src Let's create a leader for this mode: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (general-create-definer ha-irc-leader :states '(normal visual motion) :keymaps '(erc-mode-map) :prefix "SPC m" :global-prefix "" :non-normal-prefix "S-SPC") -#+END_SRC +#+end_src And a quick shortcut to call it: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-irc-leader "o" '("next channel" . erc-track-switch-buffer) "w" '("resize text" . ha-erc-resize-text) "r" '("reconnect" . ha-erc-connect-irc) "p" '("send password" . ha-erc-reconnect-password)) -#+END_SRC +#+end_src * Display Configuration Using the [[https://github.com/seagle0128/doom-modeline][Doom Modeline]] to add notifications: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq doom-modeline-irc t doom-modeline-irc-stylize 'identity) -#+END_SRC +#+end_src * Technical Artifacts :noexport: This will =provide= a code name, so that we can =require= this. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none (provide 'ha-irc) ;;; ha-irc.el ends here -#+END_SRC +#+end_src #+DESCRIPTION: A literate programming configuration file for IRC. diff --git a/ha-org-clipboard.org b/ha-org-clipboard.org index 996eddc..4db72e2 100644 --- a/ha-org-clipboard.org +++ b/ha-org-clipboard.org @@ -1,15 +1,14 @@ #+TITLE: Pasting the Org Clipboard #+AUTHOR: Howard X. Abrams #+DATE: 2020-09-15 -#+FILETAGS: :emacs: A literate programming file of functions for formatting the clipboard. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; org-clipboard --- Functions for formatting the clipboard. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,7 +22,7 @@ A literate programming file of functions for formatting the clipboard. ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC +#+end_src * Introduction I would like to paste the formatted contents of the clipboard into an Org file /as org-formatted text/. * The Clipboard @@ -32,18 +31,18 @@ Functions to help convert content from the operating system's clipboard into org Each operating system as a different way of working with the clipboard, so let's create an operating-system abstraction: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-get-clipboard () "Returns a list where the first entry is the content type, either :html or :text, and the second is the clipboard contents." (if (ha-running-on-macos?) (ha-get-mac-clipboard) (ha-get-linux-clipboard))) -#+END_SRC +#+end_src Let's define the clipboard for a Mac. The challenge here is that we need to binary unpack the data from a call to Applescript. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-get-mac-clipboard () "Returns a list where the first entry is the content type, either :html or :text, and the second is the clipboard contents." @@ -65,11 +64,11 @@ Let's define the clipboard for a Mac. The challenge here is that we need to bina (mapcar #'hex-pack-bytes)))) (decode-coding-string (mapconcat #'byte-to-string byte-seq "") 'utf-8)))) -#+END_SRC +#+end_src And define the same interface for Linux. Keep in mind, we need the exit code from calling a process, so I am going to define/use a helper function (that really should go into the piper project). -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-get-linux-clipboard () "Return the clipbaard for a Unix-based system. See `ha-get-clipboard'." (cl-destructuring-bind (exit-code contents) @@ -83,13 +82,13 @@ And define the same interface for Linux. Keep in mind, we need the exit code fro (with-temp-buffer (list (apply 'call-process program nil (current-buffer) nil args) (buffer-string)))) -#+END_SRC +#+end_src * Converting from Slack We can assume that most non-HTML text could be Slack-like: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-slack-to-markdown-buffer () "Odd function that converts Slack’s version of Markdown (where code is delimited with triple backticks) into a more formal @@ -116,23 +115,23 @@ four-space indent markdown style." (next-line) (beginning-of-line) (insert " "))))))) -#+END_SRC +#+end_src * Converting to Org Let's work top-down at this point with the interactive function that inserts the clipboard into the current buffer: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-org-yank-clipboard () "Yanks (pastes) the contents of the Apple Mac clipboard in an org-mode-compatible format." (interactive) (insert (ha-org-clipboard))) -#+END_SRC +#+end_src The heavy lifting, however is done by this function. Note that I will need another function to tidy up the output from =pandoc= that will be more to my liking. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-org-clipboard () "Return the contents of the clipboard in org-mode format." (seq-let (type contents) (ha-get-clipboard) @@ -161,22 +160,22 @@ The heavy lifting, however is done by this function. Note that I will need anoth (goto-char (point-min)) (while (re-search-forward search nil t) (replace-match replace))))) -#+END_SRC +#+end_src * Keybinding to Paste into Org Files We just need to bind it to the /local/ mode key sequence: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (with-eval-after-load 'ha-org (ha-org-leader "y" 'ha-org-yank-clipboard)) -#+END_SRC +#+end_src * Technical Artifacts :noexport: Let's provide a name so we can =require= this file: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (provide 'ha-org-clipboard) ;;; ha-org-clipboard.el ends here -#+END_SRC +#+end_src #+DESCRIPTION: A literate programming version of functions for formatting the clipboard. diff --git a/ha-org-journaling.org b/ha-org-journaling.org index 7fe4002..4aea104 100644 --- a/ha-org-journaling.org +++ b/ha-org-journaling.org @@ -5,7 +5,7 @@ A literate programming configuration file for extending the Journaling capabilities. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; org-journaling --- Configuring journals in org. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams @@ -23,11 +23,11 @@ A literate programming configuration file for extending the Journaling capabilit ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC +#+end_src * Introduction Using the [[https://github.com/bastibe/org-journal][org-journal]] project to easily create /daily/ journal entries: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package org-journal :init (setq org-journal-dir "~/journal" @@ -36,45 +36,45 @@ Using the [[https://github.com/bastibe/org-journal][org-journal]] project to eas org-journal-file-type 'daily org-journal-file-format "%Y%m%d") :config -#+END_SRC +#+end_src Notice that the rest of this file's contents is /contained/ in this =config= section! And let's put a /leader key/ sequence for it (Doom-specific): -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "f j" '("journal" . org-journal-new-entry)) -#+END_SRC +#+end_src In normal Org file, I like large headers, but in my Journal, where each task is a header, I want them smaller: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (add-hook 'org-journal-mode-hook (lambda () (set-face-attribute 'org-level-1 nil :height 1.2) (set-face-attribute 'org-level-2 nil :height 1.1) (set-face-attribute 'org-level-3 nil :height 1.0))) -#+END_SRC +#+end_src But new files could use /my formatting/ (which is different than the options available in the project): -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-auto-insert-file (rx "journal/" (zero-or-more any) (= 8 digit)) "journal") -#+END_SRC +#+end_src This depends on the following [[file:~/.doom.d/snippets/org-journal-mode/__journal][snippet/template file]]: -#+BEGIN_SRC snippet :tangle ~/other/hamacs/templates/journal +#+begin_src snippet :tangle ~/other/hamacs/templates/journal #+TITLE: Journal Entry- `(ha-journal-file-datestamp)` $0 -#+END_SRC +#+end_src Note that when I create a new journal entry, I want a title that should insert a date to match the file's name, not necessarily the current date (see below). * Journal Filename to Date Since the Journal's filename represents a date, I should be able to get the "date" associated with a file. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-journal-file-date (&optional datename) "Returns a Lisp date-timestamp based on the format of the current filename, or DATENAME if given." @@ -89,24 +89,24 @@ or DATENAME if given." (month (string-to-number (match-string 2 datename))) (year(string-to-number (match-string 1 datename)))) (encode-time 0 0 0 day month year))) -#+END_SRC +#+end_src Using the "date" associated with a file, we can create our standard timestamp: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-journal-file-datestamp (&optional datename) "Return a string of the buffer's date (based on the file's name)." (format-time-string "%e %b %Y (%A)" (ha-journal-file-date datename))) -#+END_SRC +#+end_src Close the =use-package= call: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp ) -#+END_SRC +#+end_src * Journal Capture Capturing a task (that when uncompleted, would then spillover to following days) could go to the daily journal entry. This requires a special function that opens today's journal, but specifies a non-nil prefix argument in order to inhibit inserting the heading, as =org-capture= will insert the heading. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun org-journal-find-location () (org-journal-new-entry t) (org-narrow-to-subtree) @@ -118,11 +118,11 @@ Capturing a task (that when uncompleted, would then spillover to following days) (function org-journal-find-location) "* %?\n\n %i\n\n From: %a" :empty-lines 1 :jump-to-captured t :immediate-finish t)) -#+END_SRC +#+end_src * Next and Previous File Sometimes it is obvious what is the /next file/ based on the one I'm currently reading. For instance, in my journal entries, the filename is a number that can be incremented. Same with presentation files... -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun split-string-with-number (string) "Returns a list of three components of the string, the first is the text prior to any numbers, the second is the embedded number, @@ -133,22 +133,22 @@ Sometimes it is obvious what is the /next file/ based on the one I'm currently r (list (substring string 0 start) (substring string start end) (if end (substring string end) ""))))) -#+END_SRC +#+end_src Which means that the following defines this function: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (ert-deftest split-string-with-number-test () (should (equal (split-string-with-number "abc42xyz") '("abc" "42" "xyz"))) (should (equal (split-string-with-number "42xyz") '("" "42" "xyz"))) (should (equal (split-string-with-number "abc42") '("abc" "42" ""))) (should (equal (split-string-with-number "20140424") '("" "20140424" ""))) (should (null (split-string-with-number "abcxyz")))) -#+END_SRC +#+end_src Given this splitter function, we create a function that takes some sort of operator and return a new filename based on the conversion that happens: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun find-file-number-change (f) (let* ((filename (buffer-file-name)) (parts (split-string-with-number @@ -159,11 +159,11 @@ Given this splitter function, we create a function that takes some sort of opera (nth 0 parts) new-name (nth 2 parts)))) -#+END_SRC +#+end_src And this allows us to create two simple functions that can load the "next" and "previous" files: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun find-file-increment () "Takes the current buffer, and loads the file that is 'one more' than the file contained in the current buffer. This @@ -171,9 +171,9 @@ And this allows us to create two simple functions that can load the "next" and " incremented." (interactive) (find-file (find-file-number-change '1+))) -#+END_SRC +#+end_src -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun find-file-decrement () "Takes the current buffer, and loads the file that is 'one less' than the file contained in the current buffer. This @@ -181,14 +181,14 @@ And this allows us to create two simple functions that can load the "next" and " decremented." (interactive) (find-file (find-file-number-change '1-))) -#+END_SRC +#+end_src * Technical Artifacts :noexport: Let's =provide= a name so we can =require= this file. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none (provide 'ha-org-journaling) ;;; ha-org-journaling.el ends here -#+END_SRC +#+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~ diff --git a/ha-org-publishing.org b/ha-org-publishing.org index f4a3c8e..ae10cb5 100644 --- a/ha-org-publishing.org +++ b/ha-org-publishing.org @@ -1,15 +1,14 @@ #+TITLE: Publishing my Website with Org #+AUTHOR: Howard X. Abrams #+DATE: 2020-12-22 -#+FILETAGS: :emacs: A literate programming file for publishing my website using org. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; org-publishing --- Publishing my website using org. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,24 +22,24 @@ A literate programming file for publishing my website using org. ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC +#+end_src * Introduction -While the Emacs community have a plethora of options for generating a static website from org-formatted files, I keep my pretty simple, and just use the standard =org-publish= feature. +While the Emacs community have a plethora of options for generating a static website from org-formatted files, I keep my pretty simple, and use the standard =org-publish= feature. While the following packages come with Emacs, they aren't necessarily loaded: -#+BEGIN_SRC emacs-lisp :results silent +#+begin_src emacs-lisp :results silent (require 'ox-rss) (require 'ox-publish) -#+END_SRC +#+end_src Variable settings: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq org-export-with-broken-links t org-mode-websrc-directory "/Volumes/Personal/dropbox/website" org-mode-publishing-directory (concat (getenv "HOME") "/website-public/")) -#+END_SRC +#+end_src * The Projects I separate my /website/ into distinct projects separately built: @@ -49,7 +48,7 @@ I separate my /website/ into distinct projects separately built: - =blog-rss= :: Regenerate the feeder files - =org-notes= :: Optionally render a non-web site collection of notes. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq org-publish-project-alist `(("all" :components ("blog-content" "blog-static" "org-notes" "blog-rss")) @@ -134,11 +133,11 @@ I separate my /website/ into distinct projects separately built: :publishing-directory ,(concat org-mode-publishing-directory "/other/") :recursive t :publishing-function org-publish-attachment))) -#+END_SRC +#+end_src * Including Sections In the project definitions, I reference a =pre-= and =postamble= that allow me to inject some standard HTML file headers and footers: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun org-mode-blog-preamble (options) "The function that creates the preamble top section for the blog. OPTIONS contains the property list from the org-mode export." @@ -151,10 +150,10 @@ In the project definitions, I reference a =pre-= and =postamble= that allow me t OPTIONS contains the property list from the org-mode export." (let ((base-directory (plist-get options :base-directory))) (org-babel-with-temp-filebuffer (expand-file-name "bottom.html" base-directory) (buffer-string)))) -#+END_SRC +#+end_src Another helper function for the content of website is to make sure to update =index.org=, so that the RSS gets generated. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun org-mode-blog-prepare (&optional options) "`index.org' should always be exported so touch the file before publishing." (let* ((base-directory (plist-get options :base-directory)) @@ -163,31 +162,31 @@ Another helper function for the content of website is to make sure to update =in (set-buffer-modified-p t) (save-buffer 0)) (kill-buffer buffer))) -#+END_SRC +#+end_src * Keybindings Make it easy to publish all or just some of my website: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (with-eval-after-load 'ha-org (ha-org-leader "p" '(:ignore t :which-key "publishing") "p a" '("all" . org-publish-all) "p p" '("project" . org-publish-project))) -#+END_SRC +#+end_src And let's put a /leader key/ sequence for my favorite file on my website: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "f h" '(:ignore t :which-key "howards") "f h i" '("website index" . (lambda () (find-file (expand-file-name "index.org" "~/website"))))) -#+END_SRC +#+end_src * Technical Artifacts :noexport: Let's =provide= a name so we can =require= it: -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none (provide 'ha-org-publishing) ;;; ha-org-publishing.el ends here -#+END_SRC +#+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~ diff --git a/ha-org-sprint.org b/ha-org-sprint.org index 090de1c..5538d03 100644 --- a/ha-org-sprint.org +++ b/ha-org-sprint.org @@ -1,15 +1,14 @@ #+TITLE: My Sprint Calculations and Support #+AUTHOR: Howard X. Abrams #+DATE: 2020-09-25 -#+FILETAGS: :emacs: A literate program for configuring org files for work-related notes. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; org-sprint --- Configuring org files for work-related notes. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,34 +22,32 @@ A literate program for configuring org files for work-related notes. ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC - +#+end_src * Introduction -At the beginning of each Sprint, I create a new org file dedicated to it. This workflow/technique strikes a balance between a single ever-growing file, and a thousand tiny ones. This also gives me a sense of continuity, as the filename of each sprint is date-based. +At the beginning of each Sprint, I create a new org file dedicated to it. This workflow/technique strikes a balance between a single ever-growing file, and a thousand little ones. This also gives me a sense of continuity, as the filename of each sprint is date-based. -I want a single keybinding that always displays the current Sprint note file, regardless of what Sprint it is. This means, I need to have functions that can calculate what this is. +I want a single keybinding that always displays the current Sprint note file, regardless of the Sprint. This means, I need to have functions that can calculate what this is. -In order to have the Org Capture features to be able to write to correct locations in the current file, I need each file to follow a particular format. I create a [[file:snippets/org-mode/__sprint.org][sprint note template]] that will be automatically expanded with a new sprint. +To have the Org Capture features to be able to write to correct locations in the current file, I need each file to follow a particular format. I create a [[file:snippets/org-mode/__sprint.org][sprint note template]] that will be automatically expanded with a new sprint. This template needs the following functions: - - =sprint-current-name= to be both the numeric label as well as the nickname - =sprint-date-range= to include a org-formatted date range beginning and ending the sprint - =sprint-date-from-start= return a date for pre-scheduled and recurring meetings * Naming Sprints -I give each sprint a nickname, based on a /theme/ of some sorts, alphabetized. Since our sprints are every two weeks, this allows me to go through the alphabet once. Yeah, my group likes to boringly /number/ the sprints, so I do both...mostly for myself. +I give each sprint a nickname, based on a /theme/ of some sorts, alphabetized. Since our sprints are every two weeks, this allows me to go through the alphabet once. Yeah, my group likes to boringly /number/ the sprints, so I do both… for myself. -At the beginning of the year, I choose a theme, and make a list for the upcoming sprints. In the org file, this is just a list, that gets /tangled/ into an actual Emacs LIsp list. This is pretty cool. +At the beginning of the year, I choose a theme, and make a list for the upcoming sprints. In the org file, this is a list, that gets /tangled/ into an actual Emacs LIsp list. This is pretty cool. -#+BEGIN_SRC emacs-lisp :noweb yes -(defvar sprint-nicknames - (--map (replace-regexp-in-string " *[:#].*" "" (first it)) - '<>) - "List of 26 Sprint Nicknames from A to Z.") -#+END_SRC +#+begin_src emacs-lisp :noweb yes + (defvar sprint-nicknames + (--map (replace-regexp-in-string " *[:#].*" "" (first it)) + '<>) + "List of 26 Sprint Nicknames from A to Z.") +#+end_src ** 2022 Fun sprint names for 2022 lists my favorite D&D monsters, also see [[https://list.fandom.com/wiki/List_of_monsters][this list of monsters]] from mythology and other sources: @@ -177,111 +174,103 @@ Came up with a list of somewhat well-known cities throughout the world (at least - zippy-zinder * Sprint Boundaries -Function to help in calculating dates and other features of a two-week sprint that starts on Thursday and ends on a Wednesday...hey, that is just how we do things at my job. +Function to help in calculating dates and other features of a two-week sprint that starts on Thursday and ends on a Wednesday… how we work at my job. Emacs have an internal rep of a time. - -#+BEGIN_SRC emacs-lisp -(defun get-date-time (date) - "Many functions can't deal with dates as string, so this will -parse DATE if it is a string, or return the value given otherwise." - (if (and date (stringp date)) - (->> date ; Shame that encode-time - parse-time-string ; can't take a string, as - (-take 6) ; this seems excessive... - (--map (if (null it) 0 it)) - (apply 'encode-time)) - date)) -#+END_SRC +#+begin_src emacs-lisp + (defun get-date-time (date) + "My functions can't deal with dates as string, so this will + parse DATE as a string, or return the value given otherwise." + (if (and date (stringp date)) + (->> date ; Shame that encode-time + parse-time-string ; can't take a string, as + (-take 6) ; this seems excessive... + (--map (if (null it) 0 it)) + (apply 'encode-time)) + date)) +#+end_src ** Sprint Numbering My Sprint starts on Thursday, but this sometimes changed, so let's make this a variable: +#+begin_src emacs-lisp + (defvar sprint-starting-day 2 "The day of the week the sprint begins, where 0 is Sunday.") +#+end_src -#+BEGIN_SRC emacs-lisp -(defvar sprint-starting-day 2 "The day of the week the sprint begins, where 0 is Sunday.") -#+END_SRC +We label our sprint based on the week number that it starts. Note that on a Monday, I want to consider that we are still numbering from last week. +#+begin_src emacs-lisp + (defun sprint-week-num (&optional date) + "Return the week of the current year (or DATE), but starting + the week at Thursday to Wednesday." + (let* ((d (get-date-time date)) + (dow (nth 6 (decode-time d))) ; Day of the week 0=Sunday + (week (->> d ; Week number in the year + (format-time-string "%U") + string-to-number))) + (if (>= dow sprint-starting-day) + (1+ week) + week))) +#+end_src -We label our sprint based on the week number that it starts. However, on a Monday, I want to consider that we are still numbering from last week. +Let's have these tests to make sure, and yeah, perhaps we update this at the beginning of each year. +#+begin_src emacs-lisp :tangle no + (ert-deftest sprint-week-num-test () + (should (= (sprint-week-num "2021-03-15") 11)) ;; Monday previous week + (should (= (sprint-week-num "2021-03-16") 12)) ;; Tuesday current week + (should (= (sprint-week-num "2021-03-19") 12))) +#+end_src -#+BEGIN_SRC emacs-lisp -(defun sprint-week-num (&optional date) - "Return the week of the current year (or DATE), but starting -the week at Thursday to Wednesday." - (let* ((d (get-date-time date)) - (dow (nth 6 (decode-time d))) ; Day of the week 0=Sunday - (week (->> d ; Week number in the year - (format-time-string "%U") - string-to-number))) - (if (>= dow sprint-starting-day) - (1+ week) - week))) -#+END_SRC - -Let's have a few tests to make sure, and yeah, perhaps we update this at the beginning of each year. - -#+BEGIN_SRC emacs-lisp :tangle no -(ert-deftest sprint-week-num-test () - (should (= (sprint-week-num "2021-03-15") 11)) ;; Monday previous week - (should (= (sprint-week-num "2021-03-16") 12)) ;; Tuesday current week - (should (= (sprint-week-num "2021-03-19") 12))) -#+END_SRC - -Since my sprints are currently two weeks long, we could be see that on even week numbers, the /sprint/ is actually the previous week's number. +My company has sprints two weeks long, we could be see that on even week numbers, the /sprint/ is actually the previous week's number. And it appears that my PM for this year, is a week number behind. - -#+BEGIN_SRC emacs-lisp -(defun sprint-number (&optional date) - "Return the current sprint number, with some assumptions that -each sprint is two weeks long, starting on Thursday." - (interactive) - (let ((num (sprint-week-num date))) - (if (cl-oddp num) - (- num 2) - (- num 1)))) -#+END_SRC +#+begin_src emacs-lisp + (defun sprint-number (&optional date) + "Return the current sprint number, with some assumptions that + each sprint is two weeks long, starting on Thursday." + (interactive) + (let ((num (sprint-week-num date))) + (if (cl-oddp num) + (- num 2) + (- num 1)))) +#+end_src And some tests to verify that: -#+BEGIN_SRC emacs-lisp :tangle no -(ert-deftest sprint-number-test () - (should (= (sprint-number "2021-03-15") 9)) - (should (= (sprint-number "2021-03-16") 11)) - (should (= (sprint-number "2021-03-22") 11)) - (should (= (sprint-number "2021-03-23") 11)) - (should (= (sprint-number "2021-03-29") 11)) - (should (= (sprint-number "2021-03-30") 13))) -#+END_SRC +#+begin_src emacs-lisp :tangle no + (ert-deftest sprint-number-test () + (should (= (sprint-number "2021-03-15") 9)) + (should (= (sprint-number "2021-03-16") 11)) + (should (= (sprint-number "2021-03-22") 11)) + (should (= (sprint-number "2021-03-23") 11)) + (should (= (sprint-number "2021-03-29") 11)) + (should (= (sprint-number "2021-03-30") 13))) +#+end_src ** Sprint File Name - I create my org-file notes based on the Sprint number. +#+begin_src emacs-lisp + (defun sprint-current-file (&optional date) + "Return the absolute pathname to the current sprint file." + (let ((d (get-date-time date))) + (expand-file-name + (format "~/Notes/Sprint-%s-%02d.org" + (format-time-string "%Y" d) + (sprint-number d))))) +#+end_src -#+BEGIN_SRC emacs-lisp -(defun sprint-current-file (&optional date) - "Return the absolute pathname to the current sprint file." - (let ((d (get-date-time date))) - (expand-file-name - (format "~/Notes/Sprint-%s-%02d.org" - (format-time-string "%Y" d) - (sprint-number d))))) -#+END_SRC - -So what that means, is given a particular date, I should expect to be able to find the correct Sprint file name: - -#+BEGIN_SRC emacs-lisp :tangle no -(ert-deftest sprint-current-file-test () - (should (s-ends-with? "Sprint-2019-05.org" (sprint-current-file "2019-02-07"))) - (should (s-ends-with? "Sprint-2019-05.org" (sprint-current-file "2019-02-09"))) - (should (s-ends-with? "Sprint-2019-05.org" (sprint-current-file "2019-02-10"))) - (should (s-ends-with? "Sprint-2019-05.org" (sprint-current-file "2019-02-13"))) - (should (s-ends-with? "Sprint-2019-07.org" (sprint-current-file "2019-02-14"))) - (should (s-ends-with? "Sprint-2019-07.org" (sprint-current-file "2019-02-17")))) -#+END_SRC +So given a particular date, I should expect to be able to find the correct Sprint file name: +#+begin_src emacs-lisp :tangle no + (ert-deftest sprint-current-file-test () + (should (s-ends-with? "Sprint-2019-05.org" (sprint-current-file "2019-02-07"))) + (should (s-ends-with? "Sprint-2019-05.org" (sprint-current-file "2019-02-09"))) + (should (s-ends-with? "Sprint-2019-05.org" (sprint-current-file "2019-02-10"))) + (should (s-ends-with? "Sprint-2019-05.org" (sprint-current-file "2019-02-13"))) + (should (s-ends-with? "Sprint-2019-07.org" (sprint-current-file "2019-02-14"))) + (should (s-ends-with? "Sprint-2019-07.org" (sprint-current-file "2019-02-17")))) +#+end_src Daily note-taking goes into my sprint file notes, so this interactive function makes an easy global short-cut key. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun sprint-current-find-file (&optional date) "Load the `org-mode' note associated with my current sprint." (interactive) @@ -290,140 +279,138 @@ Daily note-taking goes into my sprint file notes, so this interactive function m org-annotate-file-storage-file filename) (add-to-list 'org-agenda-files filename) (find-file filename))) -#+END_SRC +#+end_src The /name/ and /nickname/ of the sprint will be used in the =#+TITLE= section, and it looks something like: =Sprint 2019-07 (darling-dadu)= -#+BEGIN_SRC emacs-lisp -(defun sprint-current-name (&optional date) - "Return the default name of the current sprint (based on DATE)." - (let* ((d (get-date-time date)) - (sprint-order (/ (1- (sprint-number d)) 2)) - (nickname (nth sprint-order sprint-nicknames))) - (format "Sprint %s-%02d %s" - (format-time-string "%Y" d) - (sprint-number d) - nickname))) -#+END_SRC +#+begin_src emacs-lisp + (defun sprint-current-name (&optional date) + "Return the default name of the current sprint (based on DATE)." + (let* ((d (get-date-time date)) + (sprint-order (/ (1- (sprint-number d)) 2)) + (nickname (nth sprint-order sprint-nicknames))) + (format "Sprint %s-%02d %s" + (format-time-string "%Y" d) + (sprint-number d) + nickname))) +#+end_src These test won't pass any more, as the nickname of the sprint changes from year to year. -#+BEGIN_SRC emacs-lisp :tangle no -(ert-deftest sprint-current-name-test () - (should (equal "Sprint 2019-05 (candid-cannes)" (sprint-current-name "2019-02-13"))) - (should (equal "Sprint 2019-07 (darling-dadu)" (sprint-current-name "2019-02-14")))) -#+END_SRC +#+begin_src emacs-lisp :tangle no + (ert-deftest sprint-current-name-test () + (should (equal "Sprint 2019-05 (candid-cannes)" (sprint-current-name "2019-02-13"))) + (should (equal "Sprint 2019-07 (darling-dadu)" (sprint-current-name "2019-02-14")))) +#+end_src ** Sprint Start and End I want to print the beginning and ending of the sprint, where we have a sprint number or a data, and we can give the dates that bound the sprint. This odd function calculates this based on knowing the date of the /first thursday/ of the year, so I need to begin the year changing this value. I should fix this. -#+BEGIN_SRC emacs-lisp -(defun sprint-range (&optional number-or-date) - "Return a list of three entries, start of the current sprint, -end of the current sprint, and the start of the next sprint. -Each date value should be formatted with `format-time-string'." - (let* ((num (if (or (null number-or-date) (stringp number-or-date)) - (sprint-number number-or-date) - number-or-date)) - (year-start "2020-01-02") ; First Thursday of the year - (time-start (-> year-start ; Converted to time - get-date-time - float-time)) - (day-length (* 3600 24)) ; Length of day in seconds - (week-length (* day-length 7)) - (sprint-start (time-add time-start (* week-length (1- num)))) - (sprint-next (time-add time-start (* week-length (1+ num)))) - (sprint-end (time-add sprint-next (- day-length)))) - (list sprint-start sprint-end sprint-next))) -#+END_SRC +#+begin_src emacs-lisp + (defun sprint-range (&optional number-or-date) + "Return a list of three entries, start of the current sprint, + end of the current sprint, and the start of the next sprint. + Each date value should be formatted with `format-time-string'." + (let* ((num (if (or (null number-or-date) (stringp number-or-date)) + (sprint-number number-or-date) + number-or-date)) + (year-start "2020-01-02") ; First Thursday of the year + (time-start (-> year-start ; Converted to time + get-date-time + float-time)) + (day-length (* 3600 24)) ; Length of day in seconds + (week-length (* day-length 7)) + (sprint-start (time-add time-start (* week-length (1- num)))) + (sprint-next (time-add time-start (* week-length (1+ num)))) + (sprint-end (time-add sprint-next (- day-length)))) + (list sprint-start sprint-end sprint-next))) +#+end_src Format the start and end so that we can insert this directly in the org file: -#+BEGIN_SRC emacs-lisp -(defun sprint-date-range (&optional number-or-date) - "Return an `org-mode' formatted date range for a given sprint -number or date, `NUMBER-OR-DATE' or if `nil', the date range of -the current sprint." - (seq-let (sprint-start sprint-end) (sprint-range number-or-date) - (let* ((formatter "%Y-%m-%d %a") - (start (format-time-string formatter sprint-start)) - (end (format-time-string formatter sprint-end))) - (format "[%s]--[%s]" start end)))) -#+END_SRC +#+begin_src emacs-lisp + (defun sprint-date-range (&optional number-or-date) + "Return an `org-mode' formatted date range for a given sprint + number or date, `NUMBER-OR-DATE' or if `nil', the date range of + the current sprint." + (seq-let (sprint-start sprint-end) (sprint-range number-or-date) + (let* ((formatter "%Y-%m-%d %a") + (start (format-time-string formatter sprint-start)) + (end (format-time-string formatter sprint-end))) + (format "[%s]--[%s]" start end)))) +#+end_src -And let's have a test to validate this: - -#+BEGIN_SRC emacs-lisp -(ert-deftest sprint-date-range () - (should (equal (sprint-date-range 7) - (sprint-date-range "2020-02-17")))) -#+END_SRC +And validate with a test: +#+begin_src emacs-lisp + (ert-deftest sprint-date-range () + (should (equal (sprint-date-range 7) + (sprint-date-range "2020-02-17")))) +#+end_src ** Pre-scheduled Dates Due to the regularity of the sprint cadence, I can pre-schedule meetings and other deadlines by /counting/ the number of days from the start of the sprint: -#+BEGIN_SRC emacs-lisp -(defun sprint-date-from-start (days &optional formatter) - "Given a number of DAYS from the start of the sprint, return a formatted date string." - (let* ((day-length (* 3600 24)) - (start (car (sprint-range))) - (adate (time-add start (* day-length days)))) - (if formatter - (format-time-string formatter adate) - (format-time-string "%Y-%m-%d %a" adate)))) -#+END_SRC +#+begin_src emacs-lisp + (defun sprint-date-from-start (days &optional formatter) + "Given a number of DAYS from the start of the sprint, return a formatted date string." + (let* ((day-length (* 3600 24)) + (start (car (sprint-range))) + (adate (time-add start (* day-length days)))) + (if formatter + (format-time-string formatter adate) + (format-time-string "%Y-%m-%d %a" adate)))) +#+end_src * Other Date Functions The following functions /were/ helpful at times. But I'm not sure I will use them. -#+BEGIN_SRC emacs-lisp :tangle no -(defun sprint-num-days (time-interval) - "Converts a TIME-INTERVAL to a number of days." - (let ((day-length (* 3600 24))) - (round (/ (float-time time-interval) day-length)))) -#+END_SRC +#+begin_src emacs-lisp :tangle no + (defun sprint-num-days (time-interval) + "Converts a TIME-INTERVAL to a number of days." + (let ((day-length (* 3600 24))) + (round (/ (float-time time-interval) day-length)))) +#+end_src -#+BEGIN_SRC emacs-lisp :tangle no -(defun sprint-day-range (&optional date) - "Returns a list of two values, the number of days from the -start of the sprint, and the number of days to the end of the -sprint based on DATE if given, or from today if DATE is `nil'." - (seq-let (sprint-start sprint-end) (sprint-range date) - (let* ((now (get-date-time date)) - (starting (time-subtract sprint-start now)) - (ending (time-subtract sprint-end now))) - (list (sprint-num-days starting) (sprint-num-days ending))))) -#+END_SRC +#+begin_src emacs-lisp :tangle no + (defun sprint-day-range (&optional date) + "Returns a list of two values, the number of days from the + start of the sprint, and the number of days to the end of the + sprint based on DATE if given, or from today if DATE is `nil'." + (seq-let (sprint-start sprint-end) (sprint-range date) + (let* ((now (get-date-time date)) + (starting (time-subtract sprint-start now)) + (ending (time-subtract sprint-end now))) + (list (sprint-num-days starting) (sprint-num-days ending))))) +#+end_src -#+BEGIN_SRC emacs-lisp :tangle no -(ert-deftest sprint-day-range () - ;; This sprint starts on 2/13 and ends on 2/26 - (should (equal '(0 13) (sprint-day-range "2020-02-13"))) - (should (equal '(-1 12) (sprint-day-range "2020-02-14"))) - (should (equal '(-13 0) (sprint-day-range "2020-02-26")))) -#+END_SRC +#+begin_src emacs-lisp :tangle no + (ert-deftest sprint-day-range () + ;; This sprint starts on 2/13 and ends on 2/26 + (should (equal '(0 13) (sprint-day-range "2020-02-13"))) + (should (equal '(-1 12) (sprint-day-range "2020-02-14"))) + (should (equal '(-13 0) (sprint-day-range "2020-02-26")))) +#+end_src -#+BEGIN_SRC emacs-lisp :tangle no -(defun sprint-day-start (&optional date) - "Return a relative number of days to the start of the current sprint. For instance, if today was Friday, and the sprint started on Thursday, this would return -1." - (first (sprint-day-range date))) +#+begin_src emacs-lisp :tangle no + (defun sprint-day-start (&optional date) + "Return a relative number of days to the start of the current sprint. For instance, if today was Friday, and the sprint started on Thursday, this would return -1." + (first (sprint-day-range date))) -(defun sprint-day-end (&optional date) - "Return a relative number of days to the end of the current sprint. For instance, if today was Monday, and the sprint will end on Wednesday, this would return 3." - (second (sprint-day-range date))) -#+END_SRC + (defun sprint-day-end (&optional date) + "Return a relative number of days to the end of the current sprint. For instance, if today was Monday, and the sprint will end on Wednesday, this would return 3." + (second (sprint-day-range date))) +#+end_src * Technical Artifacts :noexport: -Let's provide a name so that the file can be required: - -#+BEGIN_SRC emacs-lisp :exports none -(provide 'ha-org-sprint) -;;; ha-org-sprint.el ends here -#+END_SRC +Let's =provide= a name so we can =require= this file: +#+begin_src emacs-lisp :exports none + (provide 'ha-org-sprint) + ;;; ha-org-sprint.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~ diff --git a/ha-org-word-processor.org b/ha-org-word-processor.org index 05b4d32..0e96b00 100644 --- a/ha-org-word-processor.org +++ b/ha-org-word-processor.org @@ -5,11 +5,11 @@ A literate programming file for making Org file more readable. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-org-word-processor --- Making Org file more readable. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,13 +23,13 @@ A literate programming file for making Org file more readable. ;; Using `find-file-at-point', and tangle the file to recreate this one . ;; ;;; Code: -#+END_SRC +#+end_src * Introduction -I like having org-mode files look more like a word processor than having it look like programming code. But that is just me. +I like having org-mode files look more like a word processor than having it look like programming code. But that is me. * General Org Settings Since I use ellipsis in my writing… to /change/ how org renders a collapsed heading. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq org-pretty-entities t org-ellipsis "⤵" ; …, ➡, ⚡, ▼, ↴, , ∞, ⬎, ⤷, ⤵ org-agenda-breadcrumbs-separator " ❱ " @@ -37,24 +37,24 @@ Since I use ellipsis in my writing… to /change/ how org renders a collapsed he org-special-ctrl-a/e t ; Note: Need to get this working with Evil! org-src-fontify-natively t ; Pretty code blocks org-hide-emphasis-markers t) -#+END_SRC -Oh, and as I indent lists, they should change the /bulleting/ in a particular sequence. If I begin with an =*= asterisk, I walk down the chain, but with the dashed bullets (my default choice), I just stay with dashed bullets. Numeric bullets should cycle: +#+end_src +Oh, and as I indent lists, they should change the /bulleting/ in a particular sequence. If I begin with an =*= asterisk, I walk down the chain, but with the dashed bullets (my default choice), I stay with dashed bullets. Numeric bullets should cycle: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq org-list-demote-modify-bullet '(("*" . "+") ("+" . "-") ("-" . "-") ("1." . "a.") ("a." . "1."))) -#+END_SRC +#+end_src The =org-indent-indentation-per-level=, which defaults to =2= doesn’t really work well with variable-width fonts, so let’s make the spaces at the beginning of the line fixed: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package org :custom-face (org-indent ((t (:inherit fixed-pitch))))) -#+END_SRC +#+end_src ** Typographic Quotes According to [[http://endlessparentheses.com/prettify-your-quotation-marks.html][Artur Malabarba]] of [[http://endlessparentheses.com/prettify-you-apostrophes.html][Endless Parenthesis]] blog, I type either a /straight single or double quote/, ", Emacs actually inserts Unicode rounded quotes, like “this”. This idea isn’t how a file is /displayed/, but actually how the file is /made/. Time will tell if this idea works with my auxiliary functions on my phone, like [[https://play.google.com/store/apps/details?id=com.orgzly&hl=en_US&gl=US][Orgzly]] and [[https://github.com/amake/orgro][Orgro]]. -Stealing his function, and updating it a bit, so that “quotes” just work to insert /rounded/ quotation marks: -#+BEGIN_SRC emacs-lisp +Stealing his function, and updating it a bit, so that “quotes” work to insert /rounded/ quotation marks: +#+begin_src emacs-lisp (defun ha--insert-round-quotes (opening closing) "Insert rounded quotes in prose but not inside code. The OPENING and CLOSING variables are either or . @@ -93,11 +93,11 @@ Stealing his function, and updating it a bit, so that “quotes” just work to ((looking-back starting-anew) (insert-pair)) ((looking-at existing-endq) (goto-char (match-end 0))) (t (insert closing))))))) -#+END_SRC +#+end_src Now we can take advantage of the abstraction for “double quotes”: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-round-quotes () "Insert “” and leave point in the middle. Inside a code-block, just call `self-insert-command'. @@ -106,11 +106,11 @@ Now we can take advantage of the abstraction for “double quotes”: (ha--insert-round-quotes "“" "”")) (define-key org-mode-map "\"" #'ha-round-quotes) -#+END_SRC +#+end_src And something similar for single quotes: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-apostrophe () "Insert ‘’ and leave point in the middle. Inside a code-block, just call `self-insert-command'. @@ -119,13 +119,13 @@ And something similar for single quotes: (ha--insert-round-quotes "‘" "’")) (define-key org-mode-map "'" #'ha-apostrophe) -#+END_SRC +#+end_src *Note:* I still need to worry about how quotes affect [[file:ha-org.org::*Spell Checking][spell checking]]. What would be nice, is that if I end quotes using the functions above, that if I immediately delete, I delete both pairs. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-delete-quote-pairs (&optional N) "If positioned between two quote symbols, delete the last. Used as advice to `org-delete-backward-char' function." @@ -135,10 +135,10 @@ What would be nice, is that if I end quotes using the functions above, that if I (advice-add #'org-delete-backward-char :before #'ha-delete-quote-pairs) -#+END_SRC +#+end_src Can we do the same with ellipses? -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-insert-dot-or-ellipsis () "Insert a `.' unless two have already be inserted. In this case, insert an ellipsis instead." @@ -156,10 +156,10 @@ Can we do the same with ellipses? (define-key org-mode-map "." #'ha-insert-dot-or-ellipsis) -#+END_SRC +#+end_src Now I’m getting obsessive with elongating dashes: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-insert-long-dash () "Insert a `.' unless two have already be inserted. In this case, insert an ellipsis instead." @@ -179,32 +179,32 @@ Now I’m getting obsessive with elongating dashes: (t (insert "-"))))) (define-key org-mode-map "-" #'ha-insert-long-dash) -#+END_SRC +#+end_src * Org Beautify I really want to use the Org Beautify package, but it overrides my darker themes (and all I really want is headlines to behave). First step is to make all Org header levels to use the variable font, and be the same color as the default text: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (when window-system (let ((default-color (face-attribute 'default :foreground))) (dolist (face '(org-level-1 org-level-2 org-level-3 org-level-4 org-level-5 org-level-6 org-level-7 org-level-8)) (set-face-attribute face nil :height 1.1 :foreground default-color :weight 'bold :font ha-variable-font)))) -#+END_SRC +#+end_src Next, we just need to change the header sizes: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (when window-system (set-face-attribute 'org-level-1 nil :height 2.2) (set-face-attribute 'org-level-2 nil :height 1.8) (set-face-attribute 'org-level-3 nil :height 1.4) (set-face-attribute 'org-level-4 nil :height 1.2)) -#+END_SRC +#+end_src While we are at it, let’s make sure the code blocks are using my fixed with font: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (when window-system (dolist (face '(org-block org-code org-verbatim org-table org-drawer org-table org-formula org-special-keyword org-block @@ -214,28 +214,28 @@ While we are at it, let’s make sure the code blocks are using my fixed with fo (set-face-attribute 'org-table nil :height 1.0) (set-face-attribute 'org-formula nil :height 1.0) -#+END_SRC +#+end_src Not sure why the above code removes the color of =org-verbatim=, so let’s make it stand out slightly: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (set-face-attribute 'org-verbatim nil :foreground "#aaaacc") -#+END_SRC +#+end_src And some slight adjustments to the way blocks are displayed: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (set-face-attribute 'org-block-begin-line nil :background "#282828") (set-face-attribute 'org-block nil :height 0.95) (set-face-attribute 'org-block-end-line nil :background "#262626") -#+END_SRC +#+end_src And decrease the prominence of the property drawers: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (set-face-attribute 'org-drawer nil :height 0.8) (set-face-attribute 'org-property-value nil :height 0.85) (set-face-attribute 'org-special-keyword nil :height 0.85) -#+END_SRC +#+end_src This process allows us to use =variable-pitch= mode for all org documents. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package org :hook (org-mode . variable-pitch-mode)) -#+END_SRC +#+end_src * Org Modern The [[https://github.com/minad/org-modern][org-modern]] project attempts to do a lot of what I was doing in this file. #+begin_src emacs-lisp @@ -247,8 +247,8 @@ The [[https://github.com/minad/org-modern][org-modern]] project attempts to do a #+end_src I like the smaller code blocks as well as the <2022-06-16 Thu> timestamps. * Checkboxes -#+BEGIN_SRC emacs-lisp According to an idea by [[https://jft.home.blog/2019/07/17/use-unicode-symbol-to-display-org-mode-checkboxes/][Huy Trần]], (and expanded by the [[https://github.com/minad/org-modern][org-modern]] project), we can prettify the list checkboxes. To make completed tasks more distinguishable, he changed the colors: +#+begin_src emacs-lisp (defface org-checkbox-done-text '((t (:foreground "#71696A" :strike-through t))) "Face for the text part of a checked org-mode checkbox.") @@ -297,20 +297,12 @@ However, I'm just going to have to write a function to clean this. bol (zero-or-more space) "#+END" (zero-or-more any) eol "\n") (optional bol (zero-or-more space) eol "\n")) nil t) (replace-match (match-string 1) nil :no-error))) -#+END_SRC +#+end_src Now that is some complicated regular expressions. -* Pasting -I like the idea that I will paste HTML text from the clipboard and have it converted to org-formatted text: -#+BEGIN_SRC emacs-lisp -(defun ha-org-paste () - (interactive) - (if (eq system-type 'gnu/linux) - (shell-command "xclip -t text/html -o | pandoc -r html -w org" t))) -#+END_SRC * Presentations Used to use [[https://github.com/takaxp/org-tree-slide][org-tree-slide]] for showing org files as presentations. Converted to use [[https://github.com/rlister/org-present][org-present]]. I love the /hooks/ as that makes it easier to pull out much of my =demo-it= configuration. My concern with =org-present= is that it only jumps from one top-level to another top-level header. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package org-present :init (defvar ha-org-present-mode-line mode-line-format "Cache previous mode-line format state") @@ -328,8 +320,8 @@ Used to use [[https://github.com/takaxp/org-tree-slide][org-tree-slide]] for sho (defun hide-these (patterns) (when patterns - (hide-this (car patterns)) - (hide-these (cdr patterns)))) + (hide-this (car patterns)) + (hide-these (cdr patterns)))) (save-excursion (hide-these (list (rx bol (zero-or-more space) @@ -368,13 +360,42 @@ Used to use [[https://github.com/takaxp/org-tree-slide][org-tree-slide]] for sho :hook (org-present-mode . org-present-start) (org-present-mode-quit . org-present-end)) -#+END_SRC +#+end_src * Technical Artifacts :noexport: -Let's provide a name so that the file can be required: -#+BEGIN_SRC emacs-lisp :exports none -#+END_SRC +Note, according to [[https://www.reddit.com/r/emacs/comments/vahsao/orgmode_use_capitalized_property_keywords_or/][this discussion]] (and especially [[https://scripter.co/org-keywords-lower-case/][this essay]]), I’m switching over to lower-case version of org properties. Using this helper function: +#+begin_src emacs-lisp + (defun modi/lower-case-org-keywords () + "Lower case Org keywords and block identifiers. + + Example: \"#+TITLE\" -> \"#+title\" + \"#+BEGIN_EXAMPLE\" -> \"#+begin_example\" + + Inspiration: + https://code.orgmode.org/bzg/org-mode/commit/13424336a6f30c50952d291e7a82906c1210daf0." + (interactive) + (save-excursion + (goto-char (point-min)) + (let ((case-fold-search nil) + (count 0) + ;; All keywords can be found with this expression: + ;; (org-keyword-re "\\(?1:#\\+[A-Z_]+\\(?:_[[:alpha:]]+\\)*\\)\\(?:[ :=~’”]\\|$\\)") + ;; Match examples: "#+foo bar", "#+foo:", "=#+foo=", "~#+foo~", + ;; "‘#+foo’", "“#+foo”", ",#+foo bar", + ;; "#+FOO_bar", "#+FOO". + ;; + ;; Perhap I want the #+begin_src and whatnot: + (org-keyword-re (rx line-start (optional (zero-or-more space)) + "#+" (group (or "BEGIN" "END") "_" (one-or-more alpha))))) + (while (re-search-forward org-keyword-re nil :noerror) + (setq count (1+ count)) + (replace-match (downcase (match-string-no-properties 1)) :fixedcase nil nil 1)) + (message "Lower-cased %d matches" count)))) +#+end_src +Let's provide a name so we can =require= this file: +#+begin_src emacs-lisp :exports none (provide 'ha-org-word-processor) ;;; ha-org-word-processor.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 making Org file more readable. diff --git a/ha-org.org b/ha-org.org index 156c77a..1723f83 100644 --- a/ha-org.org +++ b/ha-org.org @@ -1,15 +1,14 @@ #+TITLE: General Org-Mode Configuration #+AUTHOR: Howard X. Abrams #+DATE: 2020-09-18 -#+FILETAGS: :emacs: A literate programming file for configuring org-mode and those files. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha --- Org configuration. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -24,10 +23,10 @@ A literate programming file for configuring org-mode and those files. ;; ;;; Code: -#+END_SRC +#+end_src * Use Package Org is a /large/ complex beast with a gazillion settings, so I discuss these later in this document. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package org :mode ("\\.org" . org-mode) ; Addresses an odd warning :init @@ -47,22 +46,22 @@ Org is a /large/ complex beast with a gazillion settings, so I discuss these lat <> <> <>) -#+END_SRC +#+end_src One other helper routine is a =general= macro for org-mode files: -#+NAME: ha-org-leader -#+BEGIN_SRC emacs-lisp :tangle no +#+name: ha-org-leader +#+begin_src emacs-lisp :tangle no (general-create-definer ha-org-leader :states '(normal visual motion) :keymaps 'org-mode-map :prefix "SPC m" :global-prefix "" :non-normal-prefix "S-SPC") -#+END_SRC +#+end_src * Initialization Section Begin by initializing these org variables: -#+NAME: variables -#+BEGIN_SRC emacs-lisp :tangle no +#+name: variables +#+begin_src emacs-lisp :tangle no (setq org-return-follows-link t org-adapt-indentation nil ; Don't physically change files org-startup-indented t ; Visually show paragraphs indented @@ -90,28 +89,28 @@ Begin by initializing these org variables: org-confirm-babel-evaluate nil org-src-fontify-natively t org-src-tab-acts-natively t) -#+END_SRC +#+end_src * Configuration Section I pretend that my org files are word processing files that wrap automatically: -#+NAME: visual-hook -#+BEGIN_SRC emacs-lisp :tangle no +#+name: visual-hook +#+begin_src emacs-lisp :tangle no (add-hook 'org-mode-hook #'visual-line-mode) -#+END_SRC +#+end_src Files that end in =.txt= are still org files to me: -#+NAME: text-files -#+BEGIN_SRC emacs-lisp :tangle no +#+name: text-files +#+begin_src emacs-lisp :tangle no (add-to-list 'auto-mode-alist '("\\.txt\\'" . org-mode)) (add-to-list 'safe-local-variable-values '(org-content . 2)) -#+END_SRC +#+end_src *Note:* Org mode files with the =org-content= variable setting will collapse two levels headers. Let's allow that without the need to approve that. ** Better Return Hitting the ~Return~ key in an org file should format the following line based on context. For instance, at the end of a list, insert a new item. We begin with the interactive function that calls our code if we are at the end of the line. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-org-return () "If at the end of a line, do something special based on the information about the line by calling `ha-org-special-return', @@ -120,13 +119,13 @@ We begin with the interactive function that calls our code if we are at the end (if (eolp) (ha-org-special-return) (org-return))) -#+END_SRC +#+end_src And bind it to the Return key: -#+NAME: org-return-key -#+BEGIN_SRC emacs-lisp :tangle no +#+name: org-return-key +#+begin_src emacs-lisp :tangle no (define-key org-mode-map (kbd "RET") #'ha-org-return) -#+END_SRC +#+end_src What should we do if we are at the end of a line? - Given a prefix, call =org-return= as usual in an org file. @@ -137,7 +136,7 @@ What should we do if we are at the end of a line? I should break this function into smaller bits ... -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-org-special-return (&optional ignore) "Add new list item, heading or table row with RET. A double return on an empty element deletes it. @@ -177,11 +176,11 @@ I should break this function into smaller bits ... (t (org-return))))) -#+END_SRC +#+end_src How do we know if we are in a list item? Lists end with two blank lines, so we need to make sure we are also not at the beginning of a line to avoid a loop where a new entry gets created with one blank line. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun org-really-in-item-p () "Return item beginning position when in a plain list, nil otherwise. Unlike `org-in-item-p', this works around an issue where the @@ -192,30 +191,30 @@ How do we know if we are in a list item? Lists end with two blank lines, so we n (when location (goto-char location)) (org-in-item-p)))) -#+END_SRC +#+end_src The org API allows getting the context associated with the /current element/. This could be a line-level symbol, like paragraph or =list-item=, but always when the point isn't /inside/ a bold or italics item. You know how HTML distinguishes between /block/ and /inline/ elements, org doesn't. So, let's make a function that makes that distinction: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun org-line-element-context () "Return the symbol of the current block element, e.g. paragraph or list-item." (let ((context (org-element-context))) (while (member (car context) '(verbatim code bold italic underline)) (setq context (org-element-property :parent context))) context)) -#+END_SRC +#+end_src ** Tasks I need to add a /blocked/ state: -#+NAME: org-todo -#+BEGIN_SRC emacs-lisp :tangle no +#+name: org-todo +#+begin_src emacs-lisp :tangle no (setq org-todo-keywords '((sequence "TODO(t)" "DOING(g)" "|" "DONE(d)") (sequence "BLOCKED(b)" "|" "CANCELLED(c)"))) -#+END_SRC +#+end_src And I would like to have cute little icons for those states: -#+NAME: org-font-lock -#+BEGIN_SRC emacs-lisp +#+name: org-font-lock +#+begin_src emacs-lisp (dolist (m '(org-mode org-journal-mode)) (font-lock-add-keywords m ; A bit silly but my headers are now `(("^\\*+ \\(TODO\\) " ; shorter, and that is nice canceled @@ -233,11 +232,11 @@ And I would like to have cute little icons for those states: ;; file or the behavior). ("^ +\\([-*]\\) " (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•"))))))) -#+END_SRC +#+end_src ** Meetings I've notice that while showing a screen while taking meeting notes, I don't always like showing other windows, so I created this function to remove distractions during a meeting. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun meeting-notes () "Call this after creating an org-mode heading for where the notes for the meeting should be. After calling this function, call 'meeting-done' to reset the environment." @@ -249,11 +248,11 @@ I've notice that while showing a screen while taking meeting notes, I don't alwa (text-scale-set 2) ; readable by others (fringe-mode 0) (message "When finished taking your notes, run meeting-done.")) -#+END_SRC +#+end_src Of course, I need an 'undo' feature when the meeting is over… -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun meeting-done () "Attempt to 'undo' the effects of taking meeting notes." (interactive) @@ -261,10 +260,10 @@ Of course, I need an 'undo' feature when the meeting is over… (text-scale-set 0) ; Reset the font size increase (fringe-mode 1) (winner-undo)) ; Put the windows back in place -#+END_SRC +#+end_src ** Searching Now that my paragraphs in an org file are on a single line, I need this less, but being able to use an /indexed search system/, like [[https://ss64.com/osx/mdfind.html][mdfind]] on Macos, or [[https://www.lesbonscomptes.com/recoll/][recoll]] on Linux, gives better results that line-oriented search systems, like =grep=. Let’s create operating-system functions the command line for searching: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-search-notes--macos (phrase path) "Return the indexed search system command on MACOS, mdfind. Including the parameters using the PHRASE on the PATH(s)." @@ -279,10 +278,10 @@ Now that my paragraphs in an org file are on a single line, I need this less, bu (format "recoll -t -a -b %s" phrase)) -#+END_SRC +#+end_src This function calls these operating-system functions, but returns the matching files as a /single string/ (where each file is wrapped in single quotes, and all joined together, separated by spaces. This function also allows me to /not-match/ backup files and whatnot. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-search-notes--files (phrase path) "Return an escaped string of all files matching PHRASE. On a Mac, this search is limited by PATH" @@ -296,11 +295,11 @@ This function calls these operating-system functions, but returns the matching f (--map (format "'%s'" it)) (s-join " ")))) -#+END_SRC +#+end_src The =ha-search-notes= function prompts for the phrase to search, and then searches through the =org-directory= path to acquire the matching files. It then feeds that list to =grep= (and the [[help:grep][grep function]] in order to display a list of matches that I can jump to. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-search-notes (phrase &optional path) "Search files in PATH for PHRASE and display in a grep mode buffer." (interactive "sSearch notes for: ") @@ -311,33 +310,33 @@ The =ha-search-notes= function prompts for the phrase to search, and then search (grep (format "%s -ni -m 1 '%s' %s" command regexp files)))) -#+END_SRC +#+end_src Eventually, I would like to change the output so that the title of the Org mode is displayed instead of the first match, but that is good enough. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "f n" '("find notes" . ha-search-notes)) -#+END_SRC +#+end_src ** Misc *** Babel Blocks I use [[https://orgmode.org/worg/org-contrib/babel/intro.html][org-babel]] (obviously) and don’t need confirmation before evaluating a block: -#+NAME: ob-configuration -#+BEGIN_SRC emacs-lisp :tangle no +#+name: ob-configuration +#+begin_src emacs-lisp :tangle no (setq org-confirm-babel-evaluate nil org-src-fontify-natively t org-src-tab-acts-natively t) -#+END_SRC +#+end_src Whenever I edit Emacs Lisp blocks from my tangle-able configuration files, I get a lot of superfluous warnings. Let's turn them off. -#+NAME: no-flycheck-in-org -#+BEGIN_SRC emacs-lisp :tangle no +#+name: no-flycheck-in-org +#+begin_src emacs-lisp :tangle no (defun disable-flycheck-in-org-src-block () (setq-local flycheck-disabled-checkers '(emacs-lisp-checkdoc))) (add-hook 'org-src-mode-hook 'disable-flycheck-in-org-src-block) -#+END_SRC +#+end_src And turn on ALL the languages: -#+NAME: ob-languages -#+BEGIN_SRC emacs-lisp :tangle no +#+name: ob-languages +#+begin_src emacs-lisp :tangle no (org-babel-do-load-languages 'org-babel-load-languages '((shell . t) (js . t) @@ -348,7 +347,7 @@ And turn on ALL the languages: (dot . t) (css . t) (plantuml . t))) -#+END_SRC +#+end_src *** REST Web Services :PROPERTIES: :header-args: :var user-agent="my-super-agent" @@ -398,13 +397,13 @@ And let’s try this: *** Graphviz The [[https://graphviz.org/][graphviz project]] can be written in org blocks, and then rendered as an image: -#+NAME: ob-graphviz -#+BEGIN_SRC emacs-lisp :tangle no +#+name: ob-graphviz +#+begin_src emacs-lisp :tangle no (add-to-list 'org-src-lang-modes '("dot" . "graphviz-dot")) -#+END_SRC +#+end_src For example: -#+BEGIN_SRC dot :file support/ha-org-graphviz-example.png :exports file :results replace file +#+begin_src dot :file support/ha-org-graphviz-example.png :exports file :results replace file digraph G { graph [bgcolor=transparent]; edge [color=white]; @@ -419,25 +418,25 @@ For example: A -> H; E -> G; } -#+END_SRC +#+end_src -#+ATTR_ORG: :width 400px -#+RESULTS: +#+attr_org: :width 400px +#+results: [[file:support/ha-org-graphviz-example.png]] *** PlantUML Need to install and configure Emacs to work with [[https://plantuml.com/][PlantUML]]. Granted, this is easier now that [[http://orgmode.org/worg/org-contrib/babel][Org-Babel]] natively supports [[http://eschulte.github.io/babel-dev/DONE-integrate-plantuml-support.html][blocks of plantuml code]]. First, [[https://plantuml.com/download][download the Jar]]. -#+BEGIN_SRC sh +#+begin_src sh curl -o ~/bin/plantuml.jar https://github.com/plantuml/plantuml/releases/download/v1.2022.4/plantuml-1.2022.4.jar -#+END_SRC +#+end_src After installing the [[https://github.com/skuro/plantuml-mode][plantuml-mode]], we need to reference the location: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package plantuml-mode :straight (:host github :repo "skuro/plantuml-mode") :init (setq org-plantuml-jar-path (expand-file-name "~/bin/plantuml.jar"))) -#+END_SRC +#+end_src With some [[file:snippets/org-mode/plantuml][YASnippets]], I have = 0:16 :END: I've been working on my own [[http://www.howardism.org/Technical/Emacs/focused-work.html][approach to focused work]], -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package async) (use-package ha-focus @@ -606,23 +605,23 @@ I've been working on my own [[http://www.howardism.org/Technical/Emacs/focused-w (ha-leader "o f" '("begin focus" . ha-focus-begin) "o F" '("break focus" . ha-focus-break))) -#+END_SRC +#+end_src ** Spell Checking Let's hook some spell-checking into org files, and actually all text files. I’m making this particularly delicious. First, we turn on =abbrev-mode=. While this package comes with Emacs, check out [[https://masteringemacs.org/article/correcting-typos-misspellings-abbrev][Mickey Petersen's overview]] of using this package for auto-correcting typos. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (setq-default abbrev-mode t) -#+END_SRC +#+end_src In general, /fill/ the list, by moving the point to the /end/ of some word, and type ~C-x a g~ (or, in /normal state/, type ~SPC x d~): -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "x d" '("add abbrev" . kadd-global-abbrev)) -#+END_SRC +#+end_src The idea is that you can correct a typo /and remember/ it. Perhaps calling [[help:edit-abbrevs][edit-abbrevs]] to making any fixes to that list. Next, I create a special /auto-correcting function/ that takes advantage of Evil’s [[help:evil-prev-flyspell-error][evil-prev-flyspell-error]] to jump back to the last spelling mistake (as I often notice the mistake after entering a few words), and call the interactive [[help:ispell-word][ispell-word]]. What makes this delicious is that I then call [[help:define-global-abbrev][define-global-abbrev]] to store both the mistake and the correction so that automatically typing that mistake again, is corrected. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-fix-last-spelling (count) "Jump to the last misspelled word, and correct it. This adds the correction to the global abbrev table so that any @@ -635,12 +634,12 @@ Next, I create a special /auto-correcting function/ that takes advantage of Evil (bad-word (match-string 0))) (ispell-word) (define-global-abbrev bad-word (buffer-substring-no-properties start-word (point))))))) -#+END_SRC +#+end_src Since this auto-correction needs to happen in /insert/ mode, I have bound a few keys, including ~CMD-s~ and ~M-s~ (twice) to fixing this spelling mistake, and jumping back to where I am. If the spelling mistake is /obvious/, I use ~C-;~ to call [[help:flyspell-auto-correct-word][flyspell-auto-correct-word]]. However, I currently do not know how to use this cool feature with my =ha-fix-last-spelling= function (because I don’t know when that function is done). For this to work, we use [[https://www.emacswiki.org/emacs/FlySpell][flyspell]] mode to highlight the misspelled words, and the venerable [[https://www.emacswiki.org/emacs/InteractiveSpell][ispell]] for correcting. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package flyspell :hook (text-mode . flyspell-mode) :bind (("M-S" . ha-fix-last-spelling) ; This is j-k-s on the Moonlander. Hrm. @@ -663,12 +662,12 @@ For this to work, we use [[https://www.emacswiki.org/emacs/FlySpell][flyspell]] "s c" '("correct word" . flyspell-auto-correct-word) "s p" '("previous misspell" . evil-prev-flyspell-error) "s n" '("next misspell" . evil-next-flyspell-error))) -#+END_SRC +#+end_src Sure, the keys, ~[ s~ and ~] s~ can jump to misspelled words, and use ~M-$~ to correct them, but I'm getting used to these leaders. According to [[http://endlessparentheses.com/ispell-and-apostrophes.html][Artur Malabarba]], we can turn on rounded apostrophe's, like =‘= (left single quotation mark). The idea is to not send the quote to the sub-process: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun endless/replace-apostrophe (args) "Don't send ’ to the subprocess." (cons (replace-regexp-in-string @@ -686,13 +685,13 @@ According to [[http://endlessparentheses.com/ispell-and-apostrophes.html][Artur (cdr args)))) (advice-add #'ispell-parse-output :filter-args #'endless/replace-quote) -#+END_SRC +#+end_src The end result? No misspellings. Isn‘t this nice? Of course I need a thesaurus, and I'm installing [[https://github.com/SavchenkoValeriy/emacs-powerthesaurus][powerthesaurus]]: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package powerthesaurus :bind ("M-T" . powerthesaurus-lookup-dwim) :config @@ -702,37 +701,38 @@ Of course I need a thesaurus, and I'm installing [[https://github.com/SavchenkoV "s a" '("antonyms" . powerthesaurus-lookup-antonyms-dwim) "s r" '("related" . powerthesaurus-lookup-related-dwim) "s S" '("sentence" . powerthesaurus-lookup-sentences-dwim))) -#+END_SRC +#+end_src The key-bindings, keystrokes, and key-connections work well with ~M-T~ (notice the Shift), but to jump to specifics, we use a leader. Since the /definitions/ do not work, so let's use abo-abo's [[https://github.com/abo-abo/define-word][define-word]] project: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package define-word :config (ha-local-leader :keymaps 'text-mode-map "s d" '("define this" . define-word-at-point) "s D" '("define word" . define-word))) -#+END_SRC +#+end_src ** Grammar and Prose Linting Flagging cliches, weak phrasing and other poor grammar choices. *** Writegood The [[https://github.com/bnbeckwith/writegood-mode][writegood-mode]] is effective at highlighting passive and weasel words, but isn’t integrated into =flycheck=: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (use-package writegood-mode :hook ((org-mode . writegood-mode))) -#+END_SRC +#+end_src +And it reports obnoxious messages. We install the =write-good= NPM: -#+BEGIN_SRC shell +#+begin_src shell npm install -g write-good -#+END_SRC +#+end_src And check that the following works: -#+BEGIN_SRC sh :results output +#+begin_src sh :results output write-good --text="So it is what it is." -#+END_SRC +#+end_src Now, let’s connect it to flycheck: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package flycheck :config (flycheck-define-checker write-good @@ -743,16 +743,16 @@ Now, let’s connect it to flycheck: ((warning line-start (file-name) ":" line ":" column ":" (message) line-end)) :modes (markdown-mode org-mode text-mode)) -#+END_SRC (add-to-list 'flycheck-checkers 'write-good)) +#+end_src *** Proselint With overlapping goals to =write-good=, the [[https://github.com/amperser/proselint/][proselint]] project, once installed, can check for some English phrasings. I like =write-good= better, but I want this available for its level of /pedantic-ness/. -#+BEGIN_SRC sh +#+begin_src sh brew install proselint -#+END_SRC +#+end_src Next, create a configuration file, =~/.config/proselint/config= file, to turn on/off checks: -#+BEGIN_SRC js :tangle ~/.config/proselint/config.json :mkdirp yes +#+begin_src js :tangle ~/.config/proselint/config.json :mkdirp yes { "checks": { "typography.diacritical_marks": false, @@ -760,12 +760,11 @@ Next, create a configuration file, =~/.config/proselint/config= file, to turn on "consistency.spacing": false } } -#+END_SRC +#+end_src And tell [[https://www.flycheck.org/][flycheck]] to use this: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package flycheck -#+END_SRC :config (add-to-list 'flycheck-checkers 'proselint) ;; And create the chain of checkers so that both work: @@ -812,15 +811,16 @@ Add =textlint= to the /chain/ for Org files: :config (setq flycheck-textlint-config (format "%s/.textlintrc" (getenv "HOME"))) (flycheck-add-next-checker 'proselint 'textlint)) +#+end_src ** Distraction-Free Writing [[https://christopherfin.com/writing/emacs-writing.html][Christopher Fin's essay]] inspired me to clean my writing room. *** Write-room For a complete focused, /distraction-free/ environment, for writing or concentrating, I'm using [[https://github.com/joostkremers/writeroom-mode][Writeroom-mode]]: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package writeroom-mode :hook (writeroom-mode-disable . winner-undo) - :config + :init (ha-leader "t W" '("writeroom" . writeroom-mode)) (ha-leader :keymaps 'writeroom-mode-map "=" '("adjust width" . writeroom-adjust-width) @@ -830,23 +830,22 @@ For a complete focused, /distraction-free/ environment, for writing or concentra ("C-M-<" . writeroom-decrease-width) ("C-M->" . writeroom-increase-width) ("C-M-=" . writeroom-adjust-width))) -#+END_SRC +#+end_src *** Olivetti -#+BEGIN_SRC emacs-lisp The [[https://github.com/rnkn/olivetti][olivetti project]] sets wide margins and centers the text. It isn’t better than Writeroom, but, it works well with Logos (below). +#+begin_src emacs-lisp (use-package olivetti :init (setq-default olivetti-body-width 100) - :config (ha-leader "t O" '("olivetti" . olivetti-mode)) :bind (:map olivetti-mode-map ("C-M-<" . olivetti-shrink) ("C-M->" . olivetti-expand) ("C-M-=" . olivetti-set-width))) -#+END_SRC +#+end_src *** Logos Trying out [[https://protesilaos.com/][Protesilaos Stavrou]]’s [[https://protesilaos.com/emacs/logos][logos project]] as a replacement for [[https://github.com/joostkremers/writeroom-mode][Writeroom-mode]]: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package logos :straight (:type git :protocol ssh :host gitlab :repo "protesilaos/logos") :init @@ -870,13 +869,13 @@ Trying out [[https://protesilaos.com/][Protesilaos Stavrou]]’s [[https://prote (:states 'normal "g [" 'logos-backward-page-dwim "g ]" 'logos-forward-page-dwim)) -#+END_SRC +#+end_src * Technical Artifacts :noexport: Let's provide a name, to allow =require= to work: -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none (provide 'ha-org) ;;; ha-org.el ends here -#+END_SRC +#+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~ diff --git a/ha-passwords.org b/ha-passwords.org index 63462e0..9cb2fe4 100644 --- a/ha-passwords.org +++ b/ha-passwords.org @@ -1,15 +1,14 @@ #+TITLE: Personal Password Generator #+AUTHOR: Howard X. Abrams #+DATE: 2021-01-11 -#+FILETAGS: :emacs: A literate programming version for Emacs code to generate and store passwords. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-passwords --- Emacs code to generate and store passwords. -*- lexical-binding: t; -*- ;; ;; © 2021-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,80 +22,77 @@ A literate programming version for Emacs code to generate and store passwords. ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC +#+end_src * Introduction Let's assume that I store a bunch of words in data files: +#+begin_src emacs-lisp + (defvar ha-passwords-data-files (list (expand-file-name "adjectives.txt" + (expand-file-name "data" hamacs-source-dir)) + (expand-file-name "colors.txt" + (expand-file-name "data" hamacs-source-dir)) + (expand-file-name "nouns.txt" + (expand-file-name "data" hamacs-source-dir))) + "List of file name containing a data lines for our password generator. Order of these files matter.") -#+BEGIN_SRC emacs-lisp -(defvar ha-passwords-data-files (list (expand-file-name "adjectives.txt" - (expand-file-name "data" hamacs-source-dir)) - (expand-file-name "colors.txt" - (expand-file-name "data" hamacs-source-dir)) - (expand-file-name "nouns.txt" - (expand-file-name "data" hamacs-source-dir))) - "List of file name containing a data lines for our password generator. Order of these files matter.") - -(defvar ha-passwords-data nil - "Contains a list of lists of words that we can choose.") -#+END_SRC + (defvar ha-passwords-data nil + "Contains a list of lists of words that we can choose.") +#+end_src You can see where I'm going with this, can't you? Let's read them into list variables. +#+begin_src emacs-lisp + (defun ha-passwords--read-data-file (filename) + (with-temp-buffer + (insert-file-contents filename) + (split-string (buffer-string) "\n" t))) -#+BEGIN_SRC emacs-lisp -(defun ha-passwords--read-data-file (filename) - (with-temp-buffer - (insert-file-contents filename) - (split-string (buffer-string) "\n" t))) +#+end_src -#+END_SRC +Now we get three or so words from our list of lists: +#+begin_src emacs-lisp + (defun ha-passwords-words () + (unless ha-passwords-data + (setq ha-passwords-data + (--map (ha-passwords--read-data-file it) ha-passwords-data-files))) -Now we just get three or so words from our list of lists: - -#+BEGIN_SRC emacs-lisp -(defun ha-passwords-words () - (unless ha-passwords-data - (setq ha-passwords-data - (--map (ha-passwords--read-data-file it) ha-passwords-data-files))) - - (--map (nth (random (length it)) it) ha-passwords-data)) -#+END_SRC + (--map (nth (random (length it)) it) ha-passwords-data)) +#+end_src Let's make a password: -#+BEGIN_SRC emacs-lisp -(defun ha-passwords-generate (&optional separator) - (unless separator - (setq separator "-")) +#+begin_src emacs-lisp + (defun ha-passwords-generate (&optional separator) + (unless separator + (setq separator "-")) - (let* ((choices '("!" "@" "#" "$" "%" "^" "&" "*")) - (choice (random (length choices))) - (number (1+ choice))) - (->> (ha-passwords-words) - (s-join separator) - (s-capitalize) - (s-append (nth choice choices)) - (s-append (number-to-string number))))) -#+END_SRC + (let* ((choices '("!" "@" "#" "$" "%" "^" "&" "*")) + (choice (random (length choices))) + (number (1+ choice))) + (->> (ha-passwords-words) + (s-join separator) + (s-capitalize) + (s-append (nth choice choices)) + (s-append (number-to-string number))))) +#+end_src -#+BEGIN_SRC emacs-lisp -(defun generate-password (&optional separator) - (interactive) - (let ((passphrase (ha-passwords-generate separator))) - (kill-new passphrase) - (message "Random password: %s" passphrase))) -#+END_SRC +#+begin_src emacs-lisp + (defun generate-password (&optional separator) + (interactive) + (let ((passphrase (ha-passwords-generate separator))) + (kill-new passphrase) + (message "Random password: %s" passphrase))) +#+end_src * Keybindings Got make it easy to call: -#+BEGIN_SRC emacs-lisp -(ha-leader "a g" '("generate passwd" . generate-password)) -#+END_SRC +#+begin_src emacs-lisp + (ha-leader "a g" '("generate passwd" . generate-password)) +#+end_src * Technical Artifacts :noexport: This will =provide= a code name, so that we can =require= this. -#+BEGIN_SRC emacs-lisp :exports none -(provide 'ha-passwords) -;;; ha-passwords.el ends here -#+END_SRC +#+begin_src emacs-lisp :exports none + (provide 'ha-passwords) + ;;; ha-passwords.el ends here +#+end_src #+DESCRIPTION: A literate programming version for Emacs code to generate and store passwords. diff --git a/ha-programming-elisp.org b/ha-programming-elisp.org index 9a450f4..433c0b0 100644 --- a/ha-programming-elisp.org +++ b/ha-programming-elisp.org @@ -1,15 +1,14 @@ #+TITLE: Emacs Lisp Configuration #+AUTHOR: Howard X. Abrams #+DATE: 2022-05-11 -#+FILETAGS: :emacs: A literate programming file for configuring Emacs for Lisp programming. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-lisp --- configuring Emacs for Lisp programming. -*- lexical-binding: t; -*- ;; ;; © 2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,45 +22,45 @@ A literate programming file for configuring Emacs for Lisp programming. ;; And tangle the file to recreate this one. ;; ;;; Code: - #+END_SRC + #+end_src * Introduction -While I program in a lot of languages, I seem to be writing all my helper tools and scripts in … Emacs Lisp. So I’m cranking this up to 11. +While I program in a lot of languages, I seem to be writing all my helper tools and scripts in … Emacs Lisp. I’m cranking this up to 11. New, /non-literal/ source code comes from [[file:templates/emacs-lisp-mode.el][emacs-lisp-mode template]]: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-auto-insert-file (rx ".el" eol) "emacs-lisp-mode.el") -#+END_SRC +#+end_src * Syntax Display ** Dim those Parenthesis The [[https://github.com/tarsius/paren-face][paren-face]] project lowers the color level of parenthesis which I find better. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package paren-face :hook (emacs-lisp-mode . paren-face-mode)) -#+END_SRC +#+end_src -Show code examples with the [[https://github.com/xuchunyang/elisp-demos][elisp-demos]] package. This is really helpful. -#+BEGIN_SRC emacs-lisp +Show code examples with the [[https://github.com/xuchunyang/elisp-demos][elisp-demos]] package. +#+begin_src emacs-lisp (use-package elisp-demos :config (advice-add 'describe-function-1 :after #'elisp-demos-advice-describe-function-1)) -#+END_SRC +#+end_src * Navigation and Editing ** Goto Definitions Wilfred’s [[https://github.com/Wilfred/elisp-def][elisp-def]] project does a better job at jumping to the definition of a symbol at the point, so: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package elisp-def :hook (emacs-lisp-mode . elisp-def-mode)) -#+END_SRC +#+end_src This /should work/ with [[help:evil-goto-definition][evil-goto-defintion]], as that calls this list from [[help:evil-goto-definition-functions][evil-goto-definition-functions]]: - [[help:evil-goto-definition-imenu][evil-goto-definition-imenu]] - [[help:evil-goto-definition-semantic][evil-goto-definition-semantic]] - - [[help:evil-goto-definition-xref][evil-goto-definition-xref]] … and here is where this package will be called + - [[help:evil-goto-definition-xref][evil-goto-definition-xref]] … to show what calls a function - [[help:evil-goto-definition-search][evil-goto-definition-search]] -I love packages that add functionality but I don’t have to learn anything. However, I’m running into an issue where I do a lot of my Emacs Lisp programming in org files, and would like to jump to the function definition where it is defined in the org file. Since [[https://github.com/BurntSushi/ripgrep][ripgrep]] is pretty fast, I’ll call it instead of attempting to build a [[https://stackoverflow.com/questions/41933837/understanding-the-ctags-file-format][CTAGS]] table. Oooh, the =rg= takes a =—json= option, which makes it easier to parse. +While I love packages that add functionality and I don’t have to learn anything, I’m running into an issue where I do a lot of my Emacs Lisp programming in org files, and would like to jump to the function definition /defined in the org file/. Since [[https://github.com/BurntSushi/ripgrep][ripgrep]] is pretty fast, I’ll call it instead of attempting to build a [[https://stackoverflow.com/questions/41933837/understanding-the-ctags-file-format][CTAGS]] table. Oooh, the =rg= takes a =—json= option, which makes it easier to parse. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-org-code-block-jump (str pos) "Go to a literate org file containing a symbol, STR. The POS is ignored." @@ -86,25 +85,25 @@ I love packages that add functionality but I don’t have to learn anything. How (goto-line line)))) (add-to-list 'evil-goto-definition-functions 'ha-org-code-block-jump) -#+END_SRC +#+end_src And in case I need to call it directly: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-goto-definition () (interactive) (evil-inner-WORD)) -#+END_SRC +#+end_src ** Clever Parenthesis We need to make sure we keep the [[https://github.com/Fuco1/smartparens][smartparens]] project always in /strict mode/, because who wants to worry about paren-matching: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package smartparens :custom (smartparens-global-strict-mode t) :hook (prog-mode . smartparens-strict-mode)) -#+END_SRC +#+end_src 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 +#+begin_src emacs-lisp (use-package evil-cleverparens :after smartparens :custom @@ -118,26 +117,26 @@ The [[https://github.com/luxbock/evil-cleverparens][evil-cleverparens]] solves h :hook (prog-mode . evil-cleverparens-mode)) ;; All the languages! ;; Otherwise: (emacs-lisp-mode . evil-cleverparens-mode) -#+END_SRC +#+end_src The /trick/ to being effective with the [[https://www.emacswiki.org/emacs/ParEdit][paredit-family]] of extensions is learning the keys. The killer “app” is the slurp/barf sequence. Use the ~<~ key, in normal mode, to barf (or jettison)… in other words, /move/ the paren closer to the point. For instance: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (+ 41 (* ‖1 3)) ⟹ (+ 41 (* ‖1) 3) -#+END_SRC +#+end_src Use the ~>~ key to /slurp/ in outside objects into the current expression… in other words, move the paren away from the point. For instance: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (+ 41 (* ‖1) 3) ⟹ (+ 41 (* ‖1 3)) -#+END_SRC +#+end_src *Opening Parens.* Those two keys seem straight-forward, but they behave differently when the are on the opening parens. When the point (symbolized by ~‖~) is /on/ the opening paren, ~<~ moves the paren to the left. For instance: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (+ 41 ‖(* 1 3)) ⟹ (+ ‖(41 * 1 3)) -#+END_SRC +#+end_src And the ~>~ moves the paren to the right. For instance: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (+ 41 ‖(* 1 3)) ⟹ (+ 41 * ‖(1 3)) -#+END_SRC +#+end_src I would like to have a list of what keybindings that work in =normal= mode: - ~M-h~ / ~M-l~ move back/forward by functions @@ -159,13 +158,13 @@ The other advantage is moving around by s-expressions. This takes a little getti - ~(~ and ~)~ move up to the parent s-expression We need a real-world example. Let’s suppose we entered this: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (format "The sum of %d %d is %d" a b (+ a b)) -#+END_SRC +#+end_src But we forgot to define the =a= and =b= variables. One approach, after Escaping into the normal state, is to hit ~(~ to just to the beginning of the s-expression, and then type, ~M-(~ to wrap the expression, and type ~i~ to go into insert mode: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (‖ (format "The sum of %d %d is %d" a b (+ a b))) -#+END_SRC +#+end_src And now we can enter the =let= expression. Other nifty keybindings that I need to commit to muscle memory include: @@ -179,14 +178,14 @@ Other nifty keybindings that I need to commit to muscle memory include: ** Eval Current Expression The [[https://github.com/xiongtx/eros][eros]] package stands for Evaluation Result OverlayS for Emacs Lisp, and basically shows what each s-expression is near the cursor position instead of in the mini-buffer at the bottom of the window. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package eros :hook (emacs-lisp-mode . eros-mode)) -#+END_SRC +#+end_src A feature I enjoyed from Spacemacs is the ability to evaluate the s-expression currently containing the point. Not sure how they made it, but [[help:evil-cp-next-closing ][evil-cp-next-closing]] from cleverparens can help: -#+BEGIN_SRC emacs-lisp +#+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 @@ -197,21 +196,20 @@ finds at that point." (evil-cp-next-closing) (evil-cp-forward-sexp) (call-interactively 'eval-last-sexp))) -#+END_SRC +#+end_src And we just need to bind it. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-prog-leader "e c" '("current" . ha-eval-current-expression)) -#+END_SRC +#+end_src * Technical Artifacts :noexport: - Let's =provide= a name so we can =require= this file: -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none (provide 'ha-programming-elisp) ;;; ha-programming-elisp.el ends here - #+END_SRC + #+end_src #+DESCRIPTION: configuring Emacs for Lisp programming. diff --git a/ha-programming-python.org b/ha-programming-python.org index 350490b..13daf83 100644 --- a/ha-programming-python.org +++ b/ha-programming-python.org @@ -1,15 +1,14 @@ #+TITLE: Configuring Python in Emacs #+AUTHOR: Howard X. Abrams #+DATE: 2021-11-16 -#+FILETAGS: :emacs: A literate programming file for configuring Python. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-programming-python --- Python configuration. -*- lexical-binding: t; -*- ;; ;; © 2021-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,21 +22,21 @@ A literate programming file for configuring Python. ;; And tangle the file to recreate this one. ;; ;;; Code: - #+END_SRC + #+end_src * Introduction -The critical part of Python integration with Emacs is running LSP in Python using [[file:ha-programming.org::*direnv][direnv]]. And the only question to ask is if the Python we run it in Docker or in a virtual environment. +The critical part of Python integration with Emacs is running LSP in Python using [[file:ha-programming.org::*direnv][direnv]]. And the question to ask is if the Python we run it in Docker or in a virtual environment. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (general-create-definer ha-python-leader :states '(normal visual motion) :keymaps 'python-mode-map :prefix "SPC m" :global-prefix "" :non-normal-prefix "S-SPC") -#+END_SRC +#+end_src While Emacs supplies a Python editing environment, we’ll still use =use-package= to grab the latest: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package python :after projectile :mode ("[./]flake8\\'" . conf-mode) @@ -53,7 +52,7 @@ While Emacs supplies a Python editing environment, we’ll still use =use-packag ;; create these files for my Python projects: (add-to-list 'projectile-project-root-files "requirements-dev.txt") (add-to-list 'projectile-project-root-files "requirements-test.txt")) -#+END_SRC +#+end_src ** Virtual Environment For a local virtual machine, simply put the following in your =.envrc= file: #+begin_src conf @@ -62,9 +61,9 @@ layout_python3 That is pretty slick and simple. The old way, that we still use if you need a particular version of Python, is to install [[https://github.com/pyenv/pyenv][pyenv]] globally: -#+BEGIN_SRC sh +#+begin_src sh pip install pyenv -#+END_SRC +#+end_src And have this in your =.envrc= file: #+begin_src conf @@ -86,12 +85,12 @@ use_python() { #+end_src ** Editing Python Code Let’s integrate this [[https://github.com/wbolster/evil-text-object-python][Python support for evil-text-object]] project: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package evil-text-object-python :hook (python-mode . evil-text-object-python-add-bindings)) -#+END_SRC +#+end_src This allows me to delete a Python “block” using ~dal~. ** Docker Environment Docker really allows you to isolate your project's environment. The downside is that you are using Docker and probably a bloated container. On my work laptop, a Mac, this creates a behemoth virtual machine that immediately spins the fans like a wind tunnel. @@ -109,7 +108,7 @@ CONTAINER_EXTRA_ARGS="--env SOME_ENV_VAR=${SOME_ENV_VAR}" container_layout #+end_src ** Unit Tests -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package python-pytest :after python :commands python-pytest-dispatch @@ -123,7 +122,7 @@ container_layout "t T" '("function" . python-pytest-function) "t r" '("repeat" . python-pytest-repeat) "t p" '("dispatch" . python-pytest-dispatch))) -#+END_SRC +#+end_src ** Python Dependencies Each Python project's =requirements-dev.txt= file would reference the [[https://pypi.org/project/python-lsp-server/][python-lsp-server]] (not the /unmaintained/ project, =python-language-server=): @@ -132,7 +131,7 @@ python-lsp-server[all] #+end_src *Note:* This does mean, you would have a =tox.ini= with this line: -#+BEGIN_SRC conf +#+begin_src conf [tox] minversion = 1.6 skipsdist = True @@ -146,7 +145,7 @@ python-lsp-server[all] commands = stestr run {posargs} stestr slowest # ... -#+END_SRC +#+end_src *** Pyright I’m using the Microsoft-supported [[https://github.com/Microsoft/pyright][pyright]] package instead. Adding this to my =requirements.txt= files: #+begin_src conf :tangle no @@ -155,16 +154,16 @@ pyright The [[https://github.com/emacs-lsp/lsp-pyright][pyright package]] works with LSP. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package lsp-pyright :hook (python-mode . (lambda () (require 'lsp-pyright))) :init (when (executable-find "python3") (setq lsp-pyright-python-executable-cmd "python3"))) -#+END_SRC +#+end_src * LSP Integration of Python Now that the [[file:ha-programming.org::*Language Server Protocol (LSP) Integration][LSP Integration]] is complete, we can stitch the two projects together, by calling =lsp=. I oscillate between automatically turning on LSP mode with every Python file, but I sometimes run into issues when starting, so I turn it on with ~SPC m w s~. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package lsp-mode ;; :hook ((python-mode . lsp))) :config @@ -258,11 +257,11 @@ Now that the [[file:ha-programming.org::*Language Server Protocol (LSP) Integrat "wq" '("shutdown server" . lsp-workspace-shutdown) "wr" '("restart server" . lsp-workspace-restart) "ws" '("start server" . lsp))) -#+END_SRC +#+end_src * Project Configuration I work with a lot of projects with my team where I need to /configure/ the project such that LSP and my Emacs setup works. Let's suppose I could point a function at a project directory, and have it /set it up/: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-python-configure-project (proj-directory) "Configure PROJ-DIRECTORY for LSP and Python." (interactive "DPython Project: ") @@ -302,14 +301,14 @@ I work with a lot of projects with my team where I need to /configure/ the proje (unless (f-exists? ".dir-locals.el") (with-temp-file ".dir-locals.el" (insert "((nil . ((projectile-enable-caching . t))))"))))) -#+END_SRC +#+end_src * Technical Artifacts :noexport: Let's =provide= a name so we can =require= this file: -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none (provide 'ha-programming-python) ;;; ha-programming-python.el ends here - #+END_SRC + #+end_src #+DESCRIPTION: A literate programming file for configuring Python. diff --git a/ha-programming-scheme.org b/ha-programming-scheme.org index 7b79dd9..da0b982 100644 --- a/ha-programming-scheme.org +++ b/ha-programming-scheme.org @@ -1,15 +1,14 @@ #+TITLE: Programming in Scheme for SICP #+AUTHOR: Howard X. Abrams #+DATE: 2022-03-01 -#+FILETAGS: :emacs: A literate programming file configuring Emacs. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-programming-scheme --- Configuration for Scheme. -*- lexical-binding: t; -*- ;; ;; © 2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,22 +22,21 @@ A literate programming file configuring Emacs. ;; And tangle the file to recreate this one. ;; ;;; Code: - #+END_SRC + #+end_src * Introduction First, install MIT-Scheme, the Lisp dialect used throughout the SICP book: =brew install mit-scheme= or =sudo apt install mit-scheme= . -#+BEGIN_SRC sh +#+begin_src sh brew install mit-scheme -#+END_SRC +#+end_src Or better yet, let’s use Guile: -#+BEGIN_SRC sh +#+begin_src sh brew install guile -#+END_SRC +#+end_src * Geiser (Scheme Interface) -The [[https://www.nongnu.org/geiser/][geiser project]] attempts to be the interface between Emacs and various Schemes. Since I can’t decide which to use, I’ll install/configure them all. - -#+BEGIN_SRC emacs-lisp +The [[https://www.nongnu.org/geiser/][geiser project]] attempts to be the interface between Emacs and all the Schemes in the world. Since I can’t decide which to use, I’ll install/configure them all. +#+begin_src emacs-lisp (use-package geiser :init (setq geiser-mit-binary "/usr/local/bin/scheme" @@ -50,22 +48,22 @@ The [[https://www.nongnu.org/geiser/][geiser project]] attempts to be the interf (use-package geiser-mit) (use-package geiser-guile) (use-package geiser-racket)) -#+END_SRC +#+end_src ** Org Mode :PROPERTIES: :header-args:scheme: :session *scheming* :results value replace :END: Do we need a Scheme work for Org Babel? According to [[https://orgmode.org/worg/org-contrib/babel/languages/ob-doc-scheme.html][this document]], we just need to make sure we add the =:session= variable to start the REPL. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package ob-scheme :straight (:type built-in) :config (add-to-list 'org-babel-load-languages '(scheme . t))) -#+END_SRC +#+end_src Since the version of Scheme hasn't been updated with the deprecation, and subsequent removal of =org-babel-get-header=, we include it here: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun org-babel-get-header (params key &optional others) (delq nil (mapcar @@ -73,80 +71,80 @@ Since the version of Scheme hasn't been updated with the deprecation, and subseq params))) -#+END_SRC +#+end_src Let’s test it out by defining a variable: -#+BEGIN_SRC scheme +#+begin_src scheme (define a 42) -#+END_SRC +#+end_src And simply using it: -#+BEGIN_SRC scheme :var b=8 +#+begin_src scheme :var b=8 (+ a b) -#+END_SRC +#+end_src #+RESULTS: : ;Value: 50 And what about Scheme-specific stuff needed for SICP? -#+BEGIN_SRC scheme +#+begin_src scheme (inc 42) -#+END_SRC +#+end_src ** Install SICP :PROPERTIES: :header-args:scheme: :session sicp :results value replace :END: Let’s get the book available as an Info page: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package sicp) -#+END_SRC +#+end_src Still having difficulty getting the Scheme REPL to output the results back into this document. Let’s try Racket... Normally, I would just [[info:SICP][read the book]], however, if we want to read the [[file:~/.emacs.d/straight/build/sicp/sicp.info][sicp.info]] file, we need this, at least, temporarily: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (add-to-list 'auto-mode-alist '("\\.info\\'" . Info-mode)) -#+END_SRC +#+end_src * Racket Actually, let’s do this with [[https://racket-lang.org/][Racket]]: -#+BEGIN_SRC sh +#+begin_src sh brew install racket -#+END_SRC +#+end_src While Racket, as a Scheme, should work with Geiser (below), let’s also get [[https://racket-mode.com/][racket-mode]] working: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package racket-mode :config (setq racket-program "/usr/local/bin/racket")) -#+END_SRC +#+end_src Can we get Racket working with Org? -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package ob-racket :straight (:type git :protocol ssh :host github :repo "DEADB17/ob-racket") :after org :config (add-to-list 'org-babel-load-languages '(racket . t))) -#+END_SRC +#+end_src ** Try It Out :PROPERTIES: :HEADER-ARGS:racket: :session racketeering :results value replace :lang racket :END: Working for values? -#+BEGIN_SRC racket +#+begin_src racket (* 6 7) -#+END_SRC +#+end_src #+RESULTS: : 42 Working for output? -#+BEGIN_SRC racket :results output replace +#+begin_src racket :results output replace (define str-1 "hello") (define str-2 "world") (define all (string-join (list str-1 str-2) ", ")) (display (string-titlecase all)) -#+END_SRC +#+end_src #+RESULTS: : Hello, World @@ -158,16 +156,16 @@ The interface is horrendously slow, as the =:session= doesn’t seem to work, an :header-args:racket: :session *rsicp* :results value replace :lang sicp :END: If using [[https://docs.racket-lang.org/sicp-manual/SICP_Language.html][Racket for SICP]], install the SICP language: -#+BEGIN_SRC sh +#+begin_src sh raco pkg install --auto --update-deps sicp -#+END_SRC +#+end_src We now can give it a =#lang sicp= (or better yet, use the =:lang= header) to define certain SICP-specify features: Let’s try this now: -#+BEGIN_SRC racket +#+begin_src racket (inc 42) -#+END_SRC +#+end_src #+RESULTS: : 43 @@ -175,10 +173,10 @@ Let’s try this now: * Technical Artifacts :noexport: Let's =provide= a name so we can =require= this file: -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none (provide 'ha-programming-scheme) ;;; ha-programming-scheme.el ends here - #+END_SRC + #+end_src #+DESCRIPTION: A literate programming file configuring Emacs. diff --git a/ha-programming.org b/ha-programming.org index 29d5f2e..7e36b20 100644 --- a/ha-programming.org +++ b/ha-programming.org @@ -1,15 +1,14 @@ #+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 +#+begin_src emacs-lisp :exports none ;;; general-programming --- Configuration for general languages. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,24 +22,22 @@ A literate programming file for helping me program. ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC - +#+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 +Seems that all programming interfaces and workflows behave similarly. 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 "" :non-normal-prefix "S-SPC") -#+END_SRC +#+end_src * General The following work for all programming languages. ** direnv Farm off commands into /virtual environments/: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package direnv :init (setq direnv--executable "/usr/local/bin/direnv" @@ -48,18 +45,18 @@ Farm off commands into /virtual environments/: direnv-show-paths-in-summary t) :config (direnv-mode)) -#+END_SRC +#+end_src ** Spell Checking Comments The [[https://www.emacswiki.org/emacs/FlySpell#h5o-2][flyspell-prog-mode]] checks for misspellings in comments. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package flyspell :hook (prog-mode . flyspell-prog-mode)) -#+END_SRC +#+end_src ** Flycheck Why use [[https://www.flycheck.org/][flycheck]] over the built-in =flymake=? Speed used to be the advantage, but I’m now pushing much of this to LSP, so speed is less of an issue. What about when I am not using LSP? Also, since I’ve hooked grammar checkers, I need this with global keybindings. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package flycheck :init (setq next-error-message-highlight t) @@ -96,17 +93,17 @@ Why use [[https://www.flycheck.org/][flycheck]] over the built-in =flymake=? Spe "P v" '("verify-setup" . flycheck-verify-setup) "P x" '("disable-checker" . flycheck-disable-checker) "P t" '("toggle flycheck" . flycheck-mode))) -#+END_SRC +#+end_src ** Documentation I’ve used the [[http://kapeli.com/][Dash]] API Documentation browser (an external application) with Emacs, available for Mac. -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (use-package dash-at-point :commands (dash-at-point) :general (:states 'normal "gD" 'dash-at-point)) -#+END_SRC +#+end_src 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). Two Emacs projects compete for this position. 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 +#+begin_src emacs-lisp (use-package devdocs :after evil :general (:states 'normal "gD" 'devdocs-lookup) @@ -120,11 +117,11 @@ I’m interested in using [[https://devdocs.io/][devdocs]] instead, which is sim "d u" '("update" . devdocs-update-all) "d x" '("uninstall" . devdocs-delete) "d s" '("search" . devdocs-search))) -#+END_SRC +#+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 +#+begin_src emacs-lisp :tangle no (use-package devdocs-browser :general (:states 'normal "gD" 'devdocs-browser-open) @@ -140,10 +137,10 @@ The [[https://github.com/blahgeek/emacs-devdocs-browser][devdocs-browser]] proje "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 +#+end_src ** Code Folding -While Emacs has many options for viewing and moving around code, sometimes, it is nice to /collapse/ all functions, and then start to expand them one at a time. For this, we could enable the built-in [[https://www.emacswiki.org/emacs/HideShow][hide-show feature]]: -#+BEGIN_SRC emacs-lisp :tangle no +While Emacs has options for viewing and moving around code, sometimes, we could /collapse/ all functions, and then start to expand them one at a time. For this, we could enable the built-in [[https://www.emacswiki.org/emacs/HideShow][hide-show feature]]: +#+begin_src emacs-lisp :tangle no (use-package hide-show :straight (:type built-in) :init @@ -151,14 +148,14 @@ While Emacs has many options for viewing and moving around code, sometimes, it i hs-hide-initial-comment-block t hs-isearch-open t) :hook (prog-mode . hs-minor-mode)) -#+END_SRC -However, hide-show doesn’t work with complex YAML files. The [[https://github.com/gregsexton/origami.el][origami]] mode works better /out-of-the-box/, as it works with Python and Lisp, but falls back to indents as the format, which works really well. -#+BEGIN_SRC emacs-lisp +#+end_src +Note that =hide-show= doesn’t work with complex YAML files. The [[https://github.com/gregsexton/origami.el][origami]] mode works better /out-of-the-box/, as it works with Python and Lisp, but falls back to indents as the format, which works well. +#+begin_src emacs-lisp (use-package origami :init (setq origami-fold-replacement "⤵") :hook (prog-mode . origami-mode)) -#+END_SRC +#+end_src To take advantage of this, type: - ~z m~ :: To collapse everything - ~z r~ :: To open everything @@ -171,7 +168,7 @@ Note: Yes, we could use [[https://github.com/mrkkrp/vimish-fold][vimish-fold]] ( 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 +#+begin_src emacs-lisp (use-package lsp-mode :commands lsp :init @@ -181,12 +178,12 @@ Instead, we install [[https://emacs-lsp.github.io/lsp-mode/][LSP Mode]] (and fri 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 +#+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 +#+begin_src emacs-lisp (use-package lsp-ui :commands lsp-ui-mode :config @@ -194,15 +191,15 @@ The [[https://github.com/emacs-lsp/lsp-ui][lsp-ui]] project offers much of the d lsp-ui-sideline-show-hover t lsp-ui-sideline-show-diagnostics t) :hook (lsp-mode . lsp-ui-mode)) -#+END_SRC +#+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 +#+begin_src emacs-lisp (use-package company-lsp :config (push 'company-lsp company-backends)) -#+END_SRC +#+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. @@ -210,7 +207,7 @@ To options that might be interesting: *** 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 +#+begin_src emacs-lisp (use-package lsp-ui-imenu :straight nil :after lsp-ui @@ -219,35 +216,35 @@ The [[https://github.com/emacs-lsp/lsp-ui/blob/master/lsp-ui-imenu.el][lsp-imenu "g" '(:ignore t :which-key "goto") "g m" '("imenu" . lsp-ui-imenu)) (add-hook 'lsp-after-open-hook 'lsp-enable-imenu)) -#+END_SRC +#+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 +#+begin_src emacs-lisp (use-package lsp-treemacs :config (ha-prog-leader "0" '("treemacs" . lsp-treemacs-symbols))) -#+END_SRC +#+end_src *** Origami Folding The [[https://github.com/emacs-lsp/lsp-origami][lsp-origami]] project integrates the [[https://github.com/gregsexton/origami.el][origami]] project with LSP for /better code folding/: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package lsp-origami :hook (lsp-after-open . lsp-origami-try-enable)) -#+END_SRC +#+end_src *** Debugging Do we want to use a debugger? -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (use-package dap-mode :init (require 'dap-python)) -#+END_SRC +#+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 +#+begin_src emacs-lisp (use-package alert :init (setq alert-default-style @@ -265,20 +262,20 @@ As I've mentioned [[http://www.howardism.org/Technical/Emacs/beep-for-emacs.html compile shell-command)) (advice-add func :around #'beep-when-runs-too-long))) -#+END_SRC +#+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 +#+begin_src emacs-lisp (use-package iedit :config (ha-leader "s e" '("iedit" . iedit-mode))) -#+END_SRC +#+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 +#+begin_src emacs-lisp (defun ha-comment-line (&optional start end) (interactive "r") (when (or (null start) (not (region-active-p))) @@ -293,14 +290,14 @@ I like =comment-dwim= (~M-;~), and I like =comment-box=, but I have an odd perso (insert "\n------------------------------------------------------------------------") (comment-region (point-min) (point-max)) (widen))) -#+END_SRC +#+end_src And a keybinding: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-prog-leader "c" '("comment line" . ha-comment-line)) -#+END_SRC +#+end_src ** Evaluation While I like [[help:eval-print-last-sexp][eval-print-last-sexp]], I would like a bit of formatting in order to /keep the results/ in the file. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-eval-print-last-sexp (&optional internal-arg) "Evaluate the expression located before the point. The results are inserted back into the buffer at the end @@ -315,10 +312,10 @@ While I like [[help:eval-print-last-sexp][eval-print-last-sexp]], I would like a (dotimes (i 2) (next-line) (join-line))) -#+END_SRC +#+end_src Typical keybindings for all programming modes: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-prog-leader "e" '(:ignore t :which-key "eval") "e ;" '("expression" . eval-expression) @@ -327,10 +324,10 @@ Typical keybindings for all programming modes: "e r" '("region" . eval-region) "e e" '("last s-exp" . eval-last-sexp) "e p" '("print s-exp" . ha-eval-print-last-sexp)) -#+END_SRC +#+end_src ** Ligatures The idea of using math symbols for a programming languages keywords is /cute/, but can be confusing, so I use it sparingly: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-prettify-prog () "Extends the `prettify-symbols-alist' for programming." (mapc (lambda (pair) (push pair prettify-symbols-alist)) @@ -341,83 +338,83 @@ The idea of using math symbols for a programming languages keywords is /cute/, b (prettify-symbols-mode)) (add-hook 'prog-mode-hook 'ha-prettify-prog) -#+END_SRC +#+end_src Eventually, I want to follow [[https://www.masteringemacs.org/article/unicode-ligatures-color-emoji][Mickey Petersen's essay]] on getting full ligatures working, but right now, they don’t work on the Mac, and that is my current workhorse. ** 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 +#+begin_src emacs-lisp :tangle no (setq ivy-taskrunner-notifications-on t ivy-taskrunner-doit-bin-path "/usr/local/bin/doit") -#+END_SRC +#+end_src Doom provides basic support, but we need more keybindings: -#+BEGIN_SRC emacs-lisp :tangle no +#+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 +#+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 +#+begin_src python :tangle no def hello(): """This command greets you.""" return { 'actions': [ 'echo hello' ], } -#+END_SRC +#+end_src ** Display Configuration Using the [[https://github.com/seagle0128/doom-modeline][Doom Modeline]] to add notifications: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package doom-modeline :config (setq doom-modeline-lsp t doom-modeline-env-version t)) -#+END_SRC +#+end_src * Languages Simple to configure languages go here. More advanced stuff will go in their own files… eventually. ** Ansible Doing a lot of [[https://github.com/yoshiki/yaml-mode][YAML work]], but this project needs a new maintainer. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package yaml-mode :mode (rx ".y" (optional "a") "ml" string-end)) -#+END_SRC +#+end_src Ansible uses Jinja, so we install the [[https://github.com/paradoxxxzero/jinja2-mode][jinja2-mode]]: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package jinja2-mode :mode (rx ".j2" string-end)) -#+END_SRC +#+end_src Do I consider all YAML files an Ansible file needing [[https://github.com/k1LoW/emacs-ansible][ansible-mode]]? -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package ansible :init (setq ansible-vault-password-file "~/.ansible-vault-passfile") ;; :hook (yaml-mode . ansible-mode) :config (ha-leader "t y" 'ansible)) -#+END_SRC +#+end_src The [[help:ansible-vault-password-file][ansible-vault-password-file]] variable needs to change /per project/, so let’s use the =.dir-locals.el= file, for instance: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no ((nil . ((ansible-vault-password-file . "playbooks/.vault-password")))) -#+END_SRC +#+end_src However, let’s have all YAML files able to access Ansible’s documentation using the [[https://github.com/emacsorphanage/ansible-doc][ansible-doc]] project: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package ansible-doc :hook (yaml-mode . ansible-doc-mode) :config (ha-local-leader :keymaps 'yaml-mode-map "d" '(:ignore t :which-key "docs") "d d" 'ansible-doc)) -#+END_SRC +#+end_src The [[https://github.com/emacsmirror/poly-ansible][poly-ansible]] project uses [[https://polymode.github.io/][polymode]], gluing [[https://github.com/paradoxxxzero/jinja2-mode][jinja2-mode]] into [[https://github.com/yoshiki/yaml-mode][yaml-mode]]. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package polymode) (use-package poly-ansible @@ -425,7 +422,7 @@ The [[https://github.com/emacsmirror/poly-ansible][poly-ansible]] project uses [ :straight (:host github :repo "emacsmirror/poly-ansible") :hook ((yaml-mode . poly-ansible-mode) (poly-ansible-mode . font-lock-update))) -#+END_SRC +#+end_src ** Shell Scripts @@ -433,7 +430,7 @@ 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 +#+begin_src emacs-lisp (use-package sh-mode :straight (:type built-in) :mode (rx (or (seq ".sh" eol) @@ -443,24 +440,23 @@ While filename extensions work fine most of the time, I don't like to pre-pend = "/bin/")) "sh-mode.sh") :hook (after-save . executable-make-buffer-file-executable-if-script-p)) -#+END_SRC +#+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 +#+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 +#+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 +Provide a name 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~ diff --git a/ha-remoting.org b/ha-remoting.org index 8aa0110..e6b0d92 100644 --- a/ha-remoting.org +++ b/ha-remoting.org @@ -1,15 +1,14 @@ #+TITLE: Remote Access to Systems #+AUTHOR: Howard X. Abrams #+DATE: 2020-09-25 -#+FILETAGS: :emacs: A literate configuration for accessing remote systems. -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none ;;; ha-remoting --- Accessing remote systems. -*- lexical-binding: t; -*- ;; ;; © 2020-2022 Howard X. Abrams - ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; Licensed under a Creative Commons Attribution 4.0 International License. ;; See http://creativecommons.org/licenses/by/4.0/ ;; ;; Author: Howard X. Abrams @@ -23,14 +22,14 @@ A literate configuration for accessing remote systems. ;; And tangle the file to recreate this one. ;; ;;; Code: -#+END_SRC +#+end_src * Local Terminals The following section configures my Terminal experience, both inside and outside Emacs. ** Eshell -I used to use [[http://www.howardism.org/Technical/Emacs/eshell.html][Eshell all the time]], but now I've migrated most of /work/ directly into Emacs (rewriting all those shell scripts a Emacs Lisp code). However, a shell is pretty good for my brain at organizing files (old habits, maybe). +I used to use [[http://www.howardism.org/Technical/Emacs/eshell.html][Eshell all the time]], but now I've migrated most of /work/ directly into Emacs (rewriting all those shell scripts a Emacs Lisp code). A shell is pretty good for my brain at organizing files (old habits, maybe). First, my /aliases/ follow me around, and the following creates the alias file, =~/.emacs.d/eshell/alias=: -#+BEGIN_SRC shell :tangle (identity eshell-aliases-file) :mkdirp yes +#+begin_src shell :tangle (identity eshell-aliases-file) :mkdirp yes alias ll ls -l $* alias clear recenter 0 alias emacs 'find-file $1' @@ -38,10 +37,10 @@ First, my /aliases/ follow me around, and the following creates the alias file, alias ee 'find-file-other-window $1' alias grep 'rg -n -H --no-heading -e "$*"' alias find 'echo Please use fd instead.' # :-) -#+END_SRC +#+end_src Since that file already exists (probably), the following command may not be necessary: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package eshell :custom (eshell-kill-on-exit t) @@ -49,11 +48,11 @@ Since that file already exists (probably), the following command may not be nece :hook (eshell-exit . (lambda () (delete-window)))) -#+END_SRC +#+end_src I usually want a new window running Eshell, that is smaller than the current buffer: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun eshell-there (parent) "Open an eshell session in a PARENT directory in a smaller window named after this directory." @@ -68,10 +67,10 @@ I usually want a new window running Eshell, that is smaller than the current buf (insert (concat "ls")) (eshell-send-input))) -#+END_SRC +#+end_src We either want to start the shell in the same parent as the current buffer, or at the root of the project: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun eshell-here () "Opens up a new shell in the directory associated with the current buffer's file. Rename the eshell buffer name to match @@ -85,10 +84,10 @@ We either want to start the shell in the same parent as the current buffer, or a "Open a new shell in the project root directory, in a smaller window." (interactive) (eshell-there (projectile-project-root))) -#+END_SRC +#+end_src Add my org-specific predicates, see this [[http://www.howardism.org/Technical/Emacs/eshell-fun.html][this essay]] for the details: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun eshell-org-file-tags () "Helps the eshell parse the text the point is currently on, looking for parameters surrounded in single quotes. Returns a @@ -119,7 +118,7 @@ Add my org-specific predicates, see this [[http://www.howardism.org/Technical/Em (defvar eshell-predicate-alist nil "A list of predicates than can be applied to a globbing pattern.") (add-to-list 'eshell-predicate-alist '(?T . (eshell-org-file-tags))) -#+END_SRC +#+end_src * Remote Terminals Sure =iTerm= is nice for connecting and running commands on remote systems, however, it lacks a command line option that allows you to select and manipulate the displayed text without a mouse. This is where Emacs can shine. @@ -129,9 +128,9 @@ When calling the =ha-ssh= function, it opens a =vterm= window which, unlike othe Preload a list of favorite/special hostnames with multiple calls to: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (ha-ssh-add-favorite-host "Devbox 42" "10.0.1.42") -#+END_SRC +#+end_src Then calling =ha-ssh= function, a list of hostnames is available to quickly jump on a system (with the possibility of fuzzy matching if you have Helm or Ivy installed). @@ -145,14 +144,14 @@ Use the /favorite host/ list to quickly edit a file on a remote system using Tra Working with remote shell connections programmatically, for instance: -#+BEGIN_SRC emacs-lisp :tangle no +#+begin_src emacs-lisp :tangle no (let ((win-name "some-host")) (ha-ssh "some-host.in.some.place" win-name) (ha-ssh-send "source ~/.bash_profile" win-name) (ha-ssh-send "clear" win-name)) ;; ... (ha-ssh-exit win-name) -#+END_SRC +#+end_src Actually the =win-name= in this case is optional, as it will use a good default. @@ -163,7 +162,7 @@ I'm not giving up on Eshell, but I am playing around with [[https://github.com/a VTerm has an issue (at least for me) with ~M-Backspace~ not deleting the previous word, and yeah, I want to make sure that both keystrokes do the same thing. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package vterm :init (setq vterm-shell "/usr/local/bin/fish") @@ -178,7 +177,7 @@ VTerm has an issue (at least for me) with ~M-Backspace~ not deleting the previou (lambda () (interactive) (vterm-send-key (kbd "C-w"))))) (advice-add 'vterm-copy-mode :after 'evil-normal-state)) -#+END_SRC +#+end_src The advantage of running terminals in Emacs is the ability to copy text without a mouse. For that, hit ~C-c C-t~ to enter a special copy-mode. If I go into this mode, I might as well also go into normal mode to move the cursor. @@ -188,7 +187,7 @@ Hrm. Seems that I might want a function to copy the output of the last command t ** Variables Let's begin by defining some variables used for communication between the functions. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defvar ha-latest-ssh-window-name nil "The window-name of the latest ssh session. Most commands default to the last session.") @@ -197,19 +196,19 @@ Let's begin by defining some variables used for communication between the functi (defvar ha-ssh-favorite-hostnames '() "A list of tuples (associate list) containing a hostname and its IP address. See =ha-ssh-add-favorite-host= for easily adding to this list.") -#+END_SRC +#+end_src Also, let's make it easy for me to change my default shell: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defvar ha-ssh-shell (shell-command-to-string "type -p fish") "The executable to the shell I want to use locally.") -#+END_SRC +#+end_src ** Interactive Interface to Remote Systems The function, =ha-ssh= pops up a list of /favorite hosts/ and then uses the =vterm= functions to automatically SSH into the chosen host: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh (hostname &optional window-name) "Start a SSH session to a given HOSTNAME (with an optionally specified WINDOW-NAME). If called interactively, it presents the user with a list @@ -228,11 +227,11 @@ returned by =ha-ssh-choose-host=." (vterm-send-return)) (pop-to-buffer ha-latest-ssh-window-name)) -#+END_SRC +#+end_src Of course, we need a function that =interactive= can call to get that list, and my thought is to call =helm= if it is available, otherwise, assume that ido/ivy will take over the =completing-read= function: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh-choose-host () "Prompts the user for a host, and if it is in the cache, return its IP address, otherwise, return the input given. @@ -246,11 +245,11 @@ This is used in calls to =interactive= to select a host." :fuzzy t :history ha-ssh-host-history) (completing-read "Hostname: " ha-ssh-favorite-hostnames nil 'confirm nil 'ha-ssh-host-history)))) (alist-get hostname ha-ssh-favorite-hostnames hostname nil 'equal))) -#+END_SRC +#+end_src Simply calling =vterm= fails to load my full environment, so this allows me to start the terminal in a particular directory (defaulting to the root of the current project): -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-shell (&optional directory) "Creates and tidies up a =vterm= terminal shell in side window." (interactive (list (read-directory-name "Starting Directory: " (projectile-project-root)))) @@ -264,22 +263,22 @@ Simply calling =vterm= fails to load my full environment, so this allows me to s ;; (ha-ssh-send "source ~/.bash_profile" buf-name) ;; (ha-ssh-send "clear" buf-name) ))) -#+END_SRC +#+end_src Before we leave this section, I realize that I would like a way to /add/ to my list of hosts: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh-add-favorite-host (hostname ip-address) "Add a favorite host to your list for easy pickin's." (interactive "sHostname: \nsIP Address: ") (add-to-list 'ha-ssh-favorite-hostnames (cons hostname ip-address))) -#+END_SRC +#+end_src ** Programmatic Interface The previous functions (as well as my own end of sprint demonstrations) often need to issue some commands to a running terminal session, which is a simple wrapper around a /send text/ and /send return/ sequence: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh-send (phrase &optional window-name) "Send command PHRASE to the currently running SSH instance. If you want to refer to another session, specify the correct WINDOW-NAME. @@ -296,11 +295,11 @@ This is really useful for scripts and demonstrations." (progn (term-send-raw-string phrase) (term-send-input)))) -#+END_SRC +#+end_src On the rare occasion that I write a shell script, or at least, need to execute some one-line shell commands from some document, I have a function that combines a /read line from buffer/ and then send it to the currently running terminal: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh-send-line () "Copy the contents of the current line in the current buffer, and call =ha-ssh-send= with it. After sending the contents, it @@ -314,11 +313,11 @@ returns to the current line." (ha-ssh-send trim-cmd) ;; (sit-for 0.25) (pop-to-buffer buf))) -#+END_SRC +#+end_src Let's have a quick way to bugger out of the terminal: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh-exit (&optional window-name) "End the SSH session specified by WINDOW-NAME (or if not, the latest session)." (interactive) @@ -331,13 +330,13 @@ Let's have a quick way to bugger out of the terminal: (term-send-eof)) (kill-buffer window-name) (delete-window)) -#+END_SRC +#+end_src ** Editing Remote Files TRAMP, when it works, is amazing that we can give it a reference to a remote directory, and have =find-file= magically autocomplete. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh-find-file (hostname) "Constructs a ssh-based, tramp-focus, file reference, and then calls =find-file=." (interactive (list (ha-ssh-choose-host))) @@ -351,18 +350,18 @@ TRAMP, when it works, is amazing that we can give it a reference to a remote dir (if other-window (find-file-other-window tramp-file) (find-file tramp-file)))) -#+END_SRC +#+end_src We can even edit it as root: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh-find-root (hostname) "Constructs a ssh-based, tramp-focus, file reference, and then calls =find-file=." (interactive (list (ha-ssh-choose-host))) (let ((tramp-ssh-ref (format "/ssh:%s|sudo:%s:" hostname hostname)) (other-window (when (equal current-prefix-arg '(4)) t))) (ha-ssh--find-file tramp-ssh-ref other-window))) -#+END_SRC +#+end_src ** OpenStack Interface @@ -370,20 +369,20 @@ Instead of making sure I have a list of remote systems already in the favorite h We'll give =openstack= CLI a =--format json= option to make it easier for parsing: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (use-package json) -#+END_SRC +#+end_src Need a variable to hold all our interesting hosts. Notice I use the word /overcloud/, but this is a name I've used for years to refer to /my virtual machines/ that I can get a listing of, and not get other VMs that I don't own. -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defvar ha-ssh-overcloud-cache-data nil "A vector of associated lists containing the servers in an Overcloud.") -#+END_SRC +#+end_src If our cache data is empty, we could automatically retrieve this information, but only on the first time we attempt to connect. To do this, we'll =advice= the =ha-ssh-choose-host= function defined earlier: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh-overcloud-query-for-hosts () "If the overcloud cache hasn't be populated, ask the user if we want to run the command." (when (not ha-ssh-overcloud-cache-data) @@ -391,11 +390,11 @@ If our cache data is empty, we could automatically retrieve this information, bu (call-interactively 'ha-ssh-overcloud-cache-populate)))) (advice-add 'ha-ssh-choose-host :before 'ha-ssh-overcloud-query-for-hosts) -#+END_SRC +#+end_src We'll do the work of getting the /server list/ with this function: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh-overcloud-cache-populate (cluster) "Given an `os-cloud' entry, stores all available hostnames. Calls `ha-ssh-add-favorite-host' for each host found." @@ -412,21 +411,21 @@ We'll do the work of getting the /server list/ with this function: (alist-get 'cedev13) (seq-first)))) (message "Call to `openstack' complete. Found %d hosts." (length json-data)))) -#+END_SRC +#+end_src In case I change my virtual machines, I can repopulate that cache: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh-overcloud-cache-repopulate () "Repopulate the cache based on redeployment of my overcloud." (interactive) (setq ha-ssh-overcloud-cache-data nil) (call-interactively 'ha-ssh-overcloud-cache-populate)) -#+END_SRC +#+end_src The primary interface: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (defun ha-ssh-overcloud (hostname) "Log into an overcloud host given by HOSTNAME. Works better if you have previously run =ssh-copy-id= on the host. Remember, to @@ -446,11 +445,11 @@ Emacs), hit =C-c C-k=." (ha-ssh-send (format "export PS1='\\[\\e[34m\\]%s\\[\e[m\\] \\[\\e[33m\\]\\$\\[\\e[m\\] '" window-label)) (ha-ssh-send "clear"))) -#+END_SRC +#+end_src * Keybindings This file, so far, as been good-enough for a Vanilla Emacs installation, but to hook into Doom's leader for some sequence binding, this code isn't: -#+BEGIN_SRC emacs-lisp +#+begin_src emacs-lisp (ha-leader "a e" '("eshell" . eshell-here) "a E" '("top eshell" . eshell-project) @@ -463,15 +462,15 @@ This file, so far, as been good-enough for a Vanilla Emacs installation, but to "a s q" '("quit shell" . ha-ssh-exit) "a s f" '("find-file" . ha-ssh-find-file) "a s r" '("find-root" . ha-ssh-find-root)) -#+END_SRC +#+end_src * Technical Artifacts :noexport: Provide a name so we can =require= the file: -#+BEGIN_SRC emacs-lisp :exports none +#+begin_src emacs-lisp :exports none (provide 'ha-remoting) ;;; ha-remoting.el ends here -#+END_SRC +#+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~ diff --git a/snippets/org-mode/__hamacs b/snippets/org-mode/__hamacs index a961be4..290db4a 100644 --- a/snippets/org-mode/__hamacs +++ b/snippets/org-mode/__hamacs @@ -14,23 +14,25 @@ ${2:A literate programming file configuring Emacs.} -#+BEGIN_SRC emacs-lisp :exports none -;;; `(file-name-base (buffer-file-name)))`.el --- $2 -*- lexical-binding: t; -*- -;; -;; Copyright (C) `(format-time-string "%Y")` `user-full-name` -;; -;; Author: `user-full-name` -;; Maintainer: `user-full-name` <`user-mail-address`> -;; Created: `(format-time-string "%B %e, %Y")` -;; -;; This file is not part of GNU Emacs. -;; -;; *NB:* Do not edit this file. Instead, edit the original literate file at: -;; `(buffer-file-name)` -;; And tangle the file to recreate this one. -;; -;;; Code: -#+END_SRC +#+begin_src emacs-lisp :exports none + ;;; `(file-name-base (buffer-file-name)))`.el --- $2 -*- lexical-binding: t; -*- + ;; + ;; © `(format-time-string "%Y")` `user-full-name` + ;; Licensed under a Creative Commons Attribution 4.0 International License. + ;; See http://creativecommons.org/licenses/by/4.0/ + ;; + ;; Author: `user-full-name` + ;; Maintainer: `user-full-name` <`user-mail-address`> + ;; Created: `(format-time-string "%B %e, %Y")` + ;; + ;; While obvious, GNU Emacs does not include this file + ;; + ;; *NB:* Do not edit this file. Instead, edit the original literate file at: + ;; `(buffer-file-name)` + ;; And tangle the file to recreate this one. + ;; + ;;; Code: +#+end_src * Introduction @@ -40,12 +42,11 @@ $0 Let's provide a name so that the file can be required: -#+BEGIN_SRC emacs-lisp :exports none -(provide '`(file-name-base (buffer-file-name)))`) -;;; `(file-name-base (buffer-file-name)))`.el ends here -#+END_SRC +#+begin_src emacs-lisp :exports none + (provide '`(file-name-base (buffer-file-name)))`) + ;;; `(file-name-base (buffer-file-name)))`.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: $2 diff --git a/snippets/org-mode/code-block b/snippets/org-mode/code-block index 272c8b9..a09c304 100644 --- a/snippets/org-mode/code-block +++ b/snippets/org-mode/code-block @@ -2,6 +2,6 @@ # name: code-block # key: B}; } -#+END_SRC +#+end_src -#+ATTR_ORG: :width 800px \ No newline at end of file +#+attr_org: :width 800px \ No newline at end of file diff --git a/snippets/org-mode/header b/snippets/org-mode/header index 7aef312..55af17e 100644 --- a/snippets/org-mode/header +++ b/snippets/org-mode/header @@ -2,10 +2,10 @@ # name: header # key: header # -- -#+TITLE: ${1:`(replace-regexp-in-string "-" " " (capitalize (file-name-nondirectory (file-name-sans-extension (buffer-file-name)))))`} -#+AUTHOR: `(user-full-name)` -#+EMAIL: `user-mail-address` -#+DATE: `(format-time-string "%Y-%m-%d %B")` -#+TAGS: $2 +#+title: ${1:`(replace-regexp-in-string "-" " " (capitalize (file-name-nondirectory (file-name-sans-extension (buffer-file-name)))))`} +#+author: `(user-full-name)` +#+email: `user-mail-address` +#+date: `(format-time-string "%Y-%m-%d %B")` +#+tags: $2 $0 \ No newline at end of file diff --git a/snippets/org-mode/name b/snippets/org-mode/name index 5f3dfd3..14dcef5 100644 --- a/snippets/org-mode/name +++ b/snippets/org-mode/name @@ -2,4 +2,4 @@ # name: name # key: name # -- -#+NAME: ${0} \ No newline at end of file +#+name: ${0} \ No newline at end of file diff --git a/snippets/org-mode/plantuml b/snippets/org-mode/plantuml index 28b3098..8fa412d 100644 --- a/snippets/org-mode/plantuml +++ b/snippets/org-mode/plantuml @@ -5,12 +5,12 @@ # group: plantuml # -- -#+BEGIN_SRC plantuml :file ${1:`(ha-org-next-image-number)`}.${2:png} :exports file :results file +#+begin_src plantuml :file ${1:`(ha-org-next-image-number)`}.${2:png} :exports file :results file @startuml !include https://raw.githubusercontent.com/ptrkcsk/one-dark-plantuml-theme/v1.0.0/theme.puml $0 @enduml -#+END_SRC +#+end_src -#+ATTR_ORG: :width 800px +#+attr_org: :width 800px [[file:$1.$2]] \ No newline at end of file diff --git a/snippets/org-mode/section-property b/snippets/org-mode/section-property index 31270ea..0e8da46 100644 --- a/snippets/org-mode/section-property +++ b/snippets/org-mode/section-property @@ -2,6 +2,6 @@ # name: section-property # key: prop # -- -:PROPERTIES: +:properties: :header-args:${1:emacs-lisp} :${2:results} ${3:silent} -:END: \ No newline at end of file +:end: \ No newline at end of file diff --git a/snippets/org-mode/shell-script-code b/snippets/org-mode/shell-script-code index d724f19..3660bfe 100644 --- a/snippets/org-mode/shell-script-code +++ b/snippets/org-mode/shell-script-code @@ -2,6 +2,6 @@ # name: shell-script-code # key: ;; Maintainer: Howard X. Abrams ;; Created: `(format-time-string "%e %B %Y")` ;; -;; This file is not part of GNU Emacs. Obviously. But you knew that. +;; Obviously, GNU Emacs does not include this file in its distribution. ;; ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by diff --git a/templates/hamacs-config b/templates/hamacs-config index 8893b91..21b5d6a 100644 --- a/templates/hamacs-config +++ b/templates/hamacs-config @@ -9,38 +9,37 @@ A literate programming file for ${2:configuring Emacs.} -#+BEGIN_SRC emacs-lisp :exports none -;;; `(file-name-base (buffer-file-name)))` --- $2 -*- lexical-binding: t; -*- -;; -;; © `(format-time-string "%Y")` `user-full-name` -;; This work is licensed under a Creative Commons Attribution 4.0 International License. -;; See http://creativecommons.org/licenses/by/4.0/ -;; -;; Author: `user-full-name` -;; Maintainer: `user-full-name` -;; Created: `(format-time-string "%B %e, %Y")` -;; -;; This file is not part of GNU Emacs. -;; -;; *NB:* Do not edit this file. Instead, edit the original literate file at: -;; `(buffer-file-name)` -;; And tangle the file to recreate this one. -;; -;;; Code: -#+END_SRC +#+begin_src emacs-lisp :exports none + ;;; `(file-name-base (buffer-file-name)))` --- $2 -*- lexical-binding: t; -*- + ;; + ;; © `(format-time-string "%Y")` `user-full-name` + ;; This work is licensed under a Creative Commons Attribution 4.0 International License. + ;; See http://creativecommons.org/licenses/by/4.0/ + ;; + ;; Author: `user-full-name` + ;; Maintainer: `user-full-name` + ;; Created: `(format-time-string "%B %e, %Y")` + ;; + ;; While obvious, GNU Emacs does not include this file or project. + ;; + ;; *NB:* Do not edit this file. Instead, edit the original literate file at: + ;; `(buffer-file-name)` + ;; And tangle the file to recreate this one. + ;; + ;;; Code: +#+end_src * Introduction - $0 * Technical Artifacts :noexport: Let's =provide= a name so we can =require= this file: -#+BEGIN_SRC emacs-lisp :exports none -(provide '`(file-name-base (buffer-file-name)))`) -;;; `(file-name-base (buffer-file-name)))`.el ends here -#+END_SRC +#+begin_src emacs-lisp :exports none + (provide '`(file-name-base (buffer-file-name)))`) + ;;; `(file-name-base (buffer-file-name)))`.el ends here +#+end_src #+DESCRIPTION: $2