commit 513f2f06de344b2f6fe8156b201d00ab9588d7d8 Author: Howard Abrams Date: Mon Nov 1 17:27:14 2021 -0700 Let's go ... first commit after a major refactor Why yes, this will look like it sprung, like Athena, fully grown and in armor from my head, but this is really just the mid-point of a new endeavor. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36da315 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*~ +/bootstrap.el +/ha-config.el +/ha-display.el +/ha-org-word-processor.el +/ha-org.el diff --git a/README.org b/README.org new file mode 100644 index 0000000..e670336 --- /dev/null +++ b/README.org @@ -0,0 +1,21 @@ +#+TITLE: My Emacs Configuration +#+AUTHOR: Howard X. Abrams +#+EMAIL: howard.abrams@gmail.com +#+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). + +Why yes, feel free to steal whatever you find interesting, as sharing is what makes our community great. 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 +#+END_SRC +This creates [[file:~/.emacs.d/init.el][~/.emacs.d/init.el]] that starts the process loading the files: + + - [[file:bootstrap.org][bootstrap.org]] :: 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][ha-config.org]] :: contains /most/ of my configuration, setting up my sequence key menus, evil, etc. + - [[file:ha-display.org][ha-display.org]] :: sets up the visual aspects of an Emacs GUI, including themes, fonts and the dashboard. + - [[file:ha-org.org][ha-org.org]] :: configures the basics for org-mode formatted files. Specific features, however, come from their own files, however. + - [[file:ha-org-word-processor.org][ha-org-word-processor.org]] :: attempts to make Org files /visually/ look like what one might see in a word processor, including turning off the colors for headers, and instead increasing their size. + +*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 new file mode 100644 index 0000000..5138ac5 --- /dev/null +++ b/bootstrap.org @@ -0,0 +1,125 @@ +#+TITLE: My Emacs Bootstrap +#+AUTHOR: Howard X. Abrams +#+EMAIL: howard.abrams@gmail.com +#+DATE: 2021-10-08 +#+FILETAGS: :emacs: + +A literate programming file for bootstraping my Emacs Configuration. + +#+BEGIN_SRC emacs-lisp :exports none +;;; bootstrap.el --- file for bootstraping my Emacs Configuration +;; +;; Copyright (C) 2021 Howard X. Abrams +;; +;; Author: Howard X. Abrams +;; Maintainer: Howard X. Abrams +;; Created: October 8, 2021 +;; +;; This file is not part of GNU Emacs. +;; +;; *NB:* Do not edit this file. Instead, edit the original literate file at: +;; /Users/howard.abrams/other/hamacs/bootstrap.org +;; And tangle the file to recreate this one. +;; +;;; Code: +#+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. Here is the initialization/installation for it: + +#+BEGIN_SRC emacs-lisp +(defvar bootstrap-version) +(let ((bootstrap-file + (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory)) + (bootstrap-version 5)) + (unless (file-exists-p bootstrap-file) + (with-current-buffer + (url-retrieve-synchronously + "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el" + 'silent 'inhibit-cookies) + (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=: +#+BEGIN_SRC emacs-lisp +(straight-use-package 'use-package) +#+END_SRC +While that enables the =:straight t= extension to =use-package=, let's just have that be the default: +#+BEGIN_SRC emacs-lisp +(use-package straight + :custom (straight-use-package-by-default t)) +#+END_SRC +See the details in [[https://dev.to/jkreeftmeijer/emacs-package-management-with-straight-el-and-use-package-3oc8][this essay]]. +** Basic Libraries +The following packages come with Emacs, but seems like they still need loading: +#+BEGIN_SRC emacs-lisp +(require 'subr-x) +#+END_SRC +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 +(use-package dash) +#+END_SRC + +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 +(use-package s) +#+END_SRC + +Manipulate file paths with the [[https://github.com/rejeep/f.el][f.el]] project: +#+BEGIN_SRC emacs-lisp +(use-package f) +#+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 +(add-to-list 'load-path (f-expand "~/.emacs.d/elisp")) +#+END_SRC + +Hopefully, this will tie me over while I transition. +* 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 +(dolist (file '("ha-config.org" + "ha-display.org" + "ha-org.org" + "ha-org-word-processor.org" + ;; "org-clipboard.org" + ;; "org-journaling.org" + ;; "org-publishing.org" + ;; "org-sprint.org" + ;; "capturing-notes.org" + ;; "general-programming.org" + ;; "ha-agendas.org" + ;; "ha-email.org" + ;; "ha-irc.org" + ;; "ha-passwords.org" + ;; "ha-remoting.org" + ;; "my-feeds.org" + )) + (org-babel-load-file (f-join hamacs-source-dir file))) +#+END_SRC +We can test/debug any individual file, via: +#+BEGIN_SRC emacs-lisp + (org-babel-load-file (f-join hamacs-source-dir "ha-config.org")) +#+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 + +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 bootstrapping my environment. + +#+PROPERTY: header-args:sh :tangle no +#+PROPERTY: header-args:emacs-lisp :tangle yes +#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes + +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil +#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil +#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js diff --git a/ha-config.org b/ha-config.org new file mode 100644 index 0000000..04fe109 --- /dev/null +++ b/ha-config.org @@ -0,0 +1,1045 @@ +#+TITLE: General Emacs Configuration +#+AUTHOR: Howard X. Abrams +#+EMAIL: howard.abrams@gmail.com +#+DATE: 2020-09-10 +#+FILETAGS: :emacs: + +A literate programming file for configuring Emacs. + +#+BEGIN_SRC emacs-lisp :exports none +;;; ha-config.org --- A literate programming file for configuring Emacs. -*- lexical-binding: t; -*- +;; +;; Copyright (C) 2020-2021 Howard X. Abrams +;; +;; Author: Howard X. Abrams +;; Maintainer: Howard X. Abrams +;; Created: September 10, 2020 +;; +;; This file is not part of GNU Emacs. +;; +;; *NB:* Do not edit this file. Instead, edit the original literate file at: +;; ~/other/hamacs/ha-config.org +;; Using `find-file-at-point', and tangle the file to recreate this one . +;; +;;; Code: +#+END_SRC +* Introduction + +The following are general variable settings, and doesn't really belong to any specific purpose. + +#+BEGIN_SRC emacs-lisp +(setq user-full-name "Howard X. Abrams" + user-mail-address "howard.abrams@gmail.com") +#+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. + +#+BEGIN_SRC emacs-lisp +(setq display-line-numbers t) +(setq display-line-numbers-type 'relative) +#+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. + +#+BEGIN_SRC emacs-lisp +(setq auth-sources '("~/.authinfo.gpg") + auth-source-cache-expiry nil) +#+END_SRC + +More settings: + +#+BEGIN_SRC emacs-lisp +(setq truncate-string-ellipsis "…" ; Unicode ellispis are nicer than "..." + auto-save-default t) +#+END_SRC + +Finally, we need to make sure that the =/usr/local/bin= path is available to Emacs. Normally, we get this value from =PATH= environment variable, but I have one Emacs installation that refuses to, so... here we are. + +#+BEGIN_SRC emacs-lisp +(add-to-list 'exec-path "/usr/local/bin/") +#+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 :tangle no +(use-package piper + :load-path "~/other/emacs-piper/" + :commands shell-command-to-list ; I use this function quite a bit + :bind (:map evil-normal-state-map + ("|" . piper-user-interface))) +#+END_SRC +** Yet Another Snippet System (YASnippets) +Using [[https://github.com/joaotavora/yasnippet][yasnippet]] to convert templates into text: + +#+BEGIN_SRC emacs-lisp + (use-package yasnippet + :config + (add-to-list 'yas-snippet-dirs "~/.emacs.d/snippets") + (yas-global-mode +1)) +#+END_SRC +** Request System +The above code (and other stuff) 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.") + + :config + (defun ha-dad-joke () + "Display a random dad joke." + (interactive) + (message (ha--dad-joke))) + + (defun ha--dad-joke () + "Return string containing a dad joke from www.icanhazdadjoke.com." + (setq ha-dad-joke nil) ; Clear out old joke + (ha--dad-joke-request) + (ha--dad-joke-wait)) + + (defun ha--dad-joke-wait () + (while (not ha-dad-joke) + (sit-for 1)) + (unless ha-dad-joke + (ha--dad-joke-wait)) + ha-dad-joke) + + (defun ha--dad-joke-request () + (request "https://icanhazdadjoke.com" + :sync t + :complete (cl-function + (lambda (&key data &allow-other-keys) + (setq ha-dad-joke data)))))) +#+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 +curl -sH "Accept: text/plain" https://icanhazdadjoke.com/ +#+END_SRC +For this, I use the =request= package, which is /asynchronous/ +#+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 + (tool-bar-mode -1) + (scroll-bar-mode -1) + (horizontal-scroll-bar-mode -1) +(setq visible-bell 1) +#+END_SRC +I dislike forgetting to trim trailing white-space: + +#+BEGIN_SRC emacs-lisp +(add-hook 'before-save-hook 'delete-trailing-whitespace) +#+END_SRC + +I like being able to enable local variables in =.dir-local.el= files: +#+BEGIN_SRC emacs-lisp +(setq enable-local-variables t) +#+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]], and I'll use it (at least for a while), however, I may be jumping between the two. +#+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 + (use-package vertico-directory + :straight (el-patch :files ("~/.emacs.d/straight/repos/vertico/extensions/vertico-directory.el")) + ;; More convenient directory navigation commands + :bind (:map vertico-map + ("RET" . vertico-directory-enter) + ("DEL" . vertico-directory-delete-word) + ("M-RET" . minibuffer-force-complete-and-exit) + ("M-TAB" . minibuffer-complete)) + ;; Tidy shadowed file names + :hook (rfn-eshadow-update-overlay . vertico-directory-tidy)) +#+END_SRC +*** Selectrum +While I've been /dabbling/ in some of the alternates for =completing-read=, after watching [[https://youtu.be/lfgQC540sNM][Rari Comninos' overview]], I decided to try [[https://github.com/raxod502/selectrum][selectrum]] for better narrowing and selecting (instead of Ivy) and [[https://github.com/raxod502/prescient.el][prescient]] to order the selection from history. + +#+BEGIN_SRC emacs-lisp :tangle no + (use-package selectrum + :config + ;; Optional performance optimization by highlighting only the visible candidates. + (setq selectrum-highlight-candidates-function #'orderless-highlight-matches + orderless-skip-highlighting (lambda () selectrum-is-active)) + (selectrum-mode +1)) + + (use-package selectrum-prescient + :init + (setq selectrum-prescient-enable-filtering nil ; Use prescient on top of orderless + selectrum-prescient-enable-sorting t) + :config + (selectrum-prescient-mode +1) + (prescient-persist-mode +1)) +#+END_SRC +Keybindings: + - ~RET~ :: Select the candidate (obviously), but if directory, opens =dired= + - ~M-# RET~ :: Select =#= candidate (where # is a number 0-9) + - ~C-j~ :: Submit what you've typed (even if it would select something else) + - ~TAB~ :: Move into a directory (for =find-file=) + - ~M-w~ :: Copy the candidate to the kill ring (clipboard) + - ~,~ :: Select multiple candidates + - ~M-BKSP~ :: To go up a directory + - ~M-p~ / ~M-n~ / ~M-r~ :: Select/Search the selection history + +Wouldn't it be swell if we could quickly select one of the items visually shown. +#+BEGIN_SRC emacs-lisp :tangle no +(define-key selectrum-minibuffer-map (kbd "C-l") 'selectrum-quick-select) +#+END_SRC +*** 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 +(use-package orderless + :init + (setq completion-styles '(substring orderless) + 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. + +*** 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 +(use-package savehist + :init + (savehist-mode)) +#+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 + ;; Enable richer annotations using the Marginalia package + (use-package marginalia + :init + (setq marginalia-annotators-heavy t) + :config + (marginalia-mode)) +#+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 +(use-package which-key + :init (setq which-key-popup-type 'minibuffer) + :config (which-key-mode)) +#+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]] + +#+BEGIN_SRC emacs-lisp +(use-package evil + :init + (setq evil-disable-insert-state-bindings t + evil-want-keybinding nil + evil-want-integration t + evil-escape-key-sequence "fd" + evil-escape-unordered-key-sequence t) + + :config + (add-to-list 'evil-normal-state-modes 'shell-mode) + (add-to-list 'evil-emacs-state-modes 'term-mode) + (add-to-list 'evil-emacs-state-modes 'elfeed-search-mode) + (add-to-list 'evil-emacs-state-modes 'elfeed-show-mode) + + ;; Use escape to get out of visual mode, eh? + (evil-define-key 'visual global-map (kbd "v") 'er/expand-region) + + (evil-mode)) +#+END_SRC + +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 +(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 +** 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 +(use-package evil-collection + :after evil + :config + (evil-collection-init)) +#+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 +'(eww magit dired notmuch term wdired) +#+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 +(use-package general + :config + (general-evil-setup t) + (general-create-definer ha-leader + :keymaps '(normal insert visual emacs) + :prefix "SPC" + :global-prefix "") + + (general-create-definer ha-local-leader + :keymaps '(normal insert visual emacs) + :prefix "SPC m" + :global-prefix "")) +#+END_SRC +*** Top-Level Operations +Let's try this out with +#+BEGIN_SRC emacs-lisp + (ha-leader + "SPC" '("M-x" . execute-extended-command) + "." '("repeat" . repeat) + "X" 'org-capture + "L" 'org-store-link + "RET" 'bookmark-jump) +#+END_SRC +And ways to stop the system: +#+BEGIN_SRC emacs-lisp +(ha-leader + "q" '(:ignore t :which-key "quit/session") + "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 +*** 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)))) + +(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 + +With these helper functions in place, I can create a leader collection for file-related functions: +#+BEGIN_SRC emacs-lisp +(ha-leader + "f" '(:ignore t :which-key "files") + "f f" '("load" . find-file) + "f s" '("save" . save-buffer) + "f S" '("save as" . write-buffer) + "f SPC" '("project" . projectile-find-file) + "f r" '("recent" . recentf-open-files) + "f c" '("copy" . copy-file) + "f R" '("rename" . rename-file) + "f D" '("delete" . delete-file) + "f y" '("yank path" . ha/yank-buffer-path) + "f Y" '("yank path from project" . ha/yank-project-buffer-path) + "f d" '("dired" . dired)) +#+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 +(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 +And the collection of useful operations: +#+BEGIN_SRC emacs-lisp +(ha-leader + "b" '(:ignore t :which-key "buffers") + "b b" '("switch" . persp-switch-to-buffer) + "b B" '("switch" . switch-to-buffer-other-window) + "b o" '("other" . projectile-switch-buffer-to-other-window) + "b i" '("ibuffer" . ibuffer) + "b I" '("ibuffer" . ibuffer-other-window) + "b k" '("persp remove" . persp-remove-buffer) + "b N" '("new" . evil-buffer-new) + "b d" '("delete" . persp-kill-buffer*) + "b r" '("revert" . revert-buffer) + "b s" '("save" . save-buffer) + "b S" '("save all" . evil-write-all) + "b n" '("next" . next-buffer) + "b p" '("previous" . previous-buffer) + "b y" '("copy contents" . ha/yank-buffer-contents) + "b z" '("bury" . bury-buffer) + "b Z" '("unbury" . unbury-buffer) + + ;; And double up on the bookmarks: + "b m" '("set bookmark" . bookmark-set) + "b M" '("delete mark" . bookmark-delete)) +#+END_SRC +*** Toggle Switches +The goal here is toggle switches and other miscellaneous settings. +#+BEGIN_SRC emacs-lisp + (ha-leader + "t" '(:ignore t :which-key "toggles") + "t l" '("line numbers" . display-line-numbers-mode) + "t r" '("relative lines" . ha-toggle-relative-line-numbers)) +#+END_SRC +Really? We can't automatically toggle between relative and absolute line numbers? +#+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 +*** 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 +(use-package winner + :custom + (winner-dont-bind-my-keys t) + :config + (winner-mode +1)) +#+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 +(use-package ace-window) +#+END_SRC +This package, bound to ~SPC w w~, also allows operations specified before choosing the window: + - ~x~ - delete window + - ~m~ - swap windows + - ~M~ - move window + - ~c~ - copy window + - ~j~ - select buffer + - ~n~ - select the previous window + - ~u~ - select buffer in the other window + - ~c~ - split window fairly, either vertically or horizontally + - ~v~ - split window vertically + - ~b~ - split window horizontally + - ~o~ - maximize current window + - ~?~ - show these command bindings + + +To jump to a window even quicker, use the [[https://github.com/deb0ch/emacs-winum][winum package]]: +#+BEGIN_SRC emacs-lisp +(use-package winum + :config + (winum-mode +1)) +#+END_SRC +The ~0~ key/window should be always associated with a project-specific tree window: +#+BEGIN_SRC emacs-lisp +(add-to-list 'winum-assign-functions + (lambda () + (when (string-match-p (buffer-name) ".*\\*NeoTree\\*.*") 10))) +#+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 +(use-package hydra + :config + (defhydra hydra-window-resize (:color blue :hint nil) " +_w_: select _n_: new _^_: taller (t) _z_: Swap _+_: text larger +_c_: cycle _d_: delete _V_: shorter (T) _u_: undo _-_: text smaller +_j_: go up _=_: balance _>_: wider _U_: undo+ +_k_: down _m_: maximize _<_: narrower _r_: redo +_h_: left _s_: h-split _e_: balanced _R_: redo+ +_l_: right _v_: v-split _o_: choose by number (also 0-9) +" + ("w" ace-window) + ("c" other-window) + ("=" balance-windows) + ("m" delete-other-windows) + ("d" delete-window) + ("D" ace-delete-window) + + ("z" ace-window-swap) + ("u" winner-undo) + ("U" winner-undo :color pink) + ("C-r" winner-redo) + ("r" winner-redo) + ("R" winner-redo :color pink) + + ("n" evil-window-new) + ("j" evil-window-up) + ("k" evil-window-down) + ("h" evil-window-left) + ("l" evil-window-right) + + ("s" evil-window-split) + ("v" evil-window-vsplit) + + ("+" text-scale-increase :color pink) + ("=" text-scale-increase :color pink) + ("-" text-scale-decrease :color pink) + ("^" evil-window-increase-height :color pink) + ("V" evil-window-decrease-height :color pink) + ("t" evil-window-increase-height :color pink) + ("T" evil-window-decrease-height :color pink) + (">" evil-window-increase-width :color pink) + ("<" evil-window-decrease-width :color pink) + ("e" balance-windows) + + ("o" winum-select-window-by-number) + ("0" winum-select-window-0-or-10) + ("1" winum-select-window-1) + ("2" winum-select-window-2) + ("3" winum-select-window-3) + ("4" winum-select-window-4) + ("5" winum-select-window-5) + ("6" winum-select-window-6) + ("7" winum-select-window-7) + ("8" winum-select-window-8) + ("9" winum-select-window-9) + + ;; Extra bindings: + ("t" evil-window-increase-height :color pink) + ("T" evil-window-decrease-height :color pink) + ("." evil-window-increase-width :color pink) + ("," evil-window-decrease-width :color pink) + ("q" nil :color blue))) + +(ha-leader "w" '("windows" . hydra-window-resize/body)) +#+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. +#+BEGIN_SRC emacs-lisp +(use-package rg + :config + (ha-leader + "s" '(:ignore t :which-key "search") + "s p" 'projectile-ripgrep)) +#+END_SRC + +*** Text Operations +Stealing much of this from Spacemacs. +#+BEGIN_SRC emacs-lisp +(ha-leader + "x" '(:ignore t :which-key "text") + "x q" '("fill paragraph" . fill-paragraph)) +#+END_SRC +*** Help Operations +While the ~C-h~ is easy enough, I am now in the habit of typing ~SPC h~ instead. +#+BEGIN_SRC emacs-lisp + (ha-leader + "h" '(:ignore t :which-key "help") + "h f" '("function" . describe-function) + "h v" '("variable" . describe-variable) + "h k" '("key binding" . describe-key) + "h B" '("embark" . embark-bindings)) +#+END_SRC + +*** Consult Enhancements +The [[https://github.com/minad/consult][consult]] package is a replacement for selecting buffers and other /speciality functions/, similar to the [[https://oremacs.com/2015/04/09/counsel-completion/][Ivy's counsel completion]] project. I think I may be adding it sparingly, as personally, I read files and buffers based on the selected /project/. + +The pattern is to add the /consult/ functions to my standard general leader organization, but they will all end with ~TAB~ (unique, easy and consistent). +#+BEGIN_SRC emacs-lisp +(use-package consult + :config + (ha-leader + "b TAB" '("consult buffer" . consult-buffer) + "b S-TAB" '("consult buffer in window" . consult-buffer-other-window) + "s TAB" '("consult search" . consult-ripgrep) + "f TAB" '("consult file" . consult-file))) +#+END_SRC +*** 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 + (use-package embark + :bind + (("s-;" . embark-act) ; Work in minibuffer and elsewhere + ("s-/" . embark-dwim)) + + :init + ;; Optionally replace the key help with a completing-read interface + (setq prefix-help-command #'embark-prefix-help-command)) +#+END_SRC +Consult users will also want the embark-consult package. +#+BEGIN_SRC emacs-lisp +(use-package embark-consult + :after (embark consult) + :demand t ; only necessary if you have the hook below + ;; if you want to have consult previews as you move around an + ;; auto-updating embark collect buffer + :hook + (embark-collect-mode . consult-preview-at-point-mode)) +#+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: + +#+BEGIN_SRC emacs-lisp +(use-package evil-snipe + :init + (setq evil-snipe-scope 'visible) + :config + (evil-define-key '(normal motion operator visual) + "s" #'evil-snipe-s + "S" #'evil-snipe-S) + (evil-snipe-mode +1)) +#+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 calls surround. As an original Emacs person, I am not bound by that key history, but I do need them consistent: + +#+BEGIN_SRC emacs-lisp +(dolist (state '(normal motion operator visual)) + (evil-define-key state evil-surround-mode-map "z" 'evil-surround-edit) + (evil-define-key state evil-surround-mode-map "Z" 'evil-Surround-edit)) +#+END_SRC + +** Jump, Jump, Jump! + +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. + +#+BEGIN_SRC emacs-lisp +(use-package avy + :init + (setq avy-all-windows t + avy-single-candidate-jump t + avy-orders-alist + '((avy-goto-char . avy-order-closest) + (avy-goto-word-0 . avy-order-closest))) + :config (ha-leader "j" '("jump" . avy-goto-char-timer)) + :bind ("" . avy-goto-char-timer)) +#+END_SRC +*Note:* The links should be shorter near the point as opposed to starting from the top of the window. +** Miscellaneous Keys +I really appreciated the [[https://github.com/benma/visual-regexp.el][visual-regexp package]]: + +#+BEGIN_SRC emacs-lisp +(use-package visual-regexp + :bind (("C-c r" . vr/replace) + ("C-c q" . vr/query-replace))) +#+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). + +#+BEGIN_SRC emacs-lisp +(use-package projectile + :custom + (projectile-sort-order 'recentf) + :config + (ha-leader + "p" '(:ignore t :which-key "projects") + "p W" '("initialize workspace" . ha-workspace-initialize) + "p n" '("new project space" . ha-project-persp) + "p !" '("run cmd in project root" . projectile-run-shell-command-in-root) + "p &" '("async cmd in project root" . projectile-run-async-shell-command-in-root) + "p a" '("add new project" . projectile-add-known-project) + "p b" '("switch to project buffer" . projectile-switch-to-buffer) + "p c" '("compile in project" . projectile-compile-project) + "p C" '("repeat last command" . projectile-repeat-last-command) + "p d" '("remove known project" . projectile-remove-known-project) + "p e" '("edit project .dir-locals" . projectile-edit-dir-locals) + "p f" '("find file in project" . projectile-find-file) + "p g" '("configure project" . projectile-configure-project) + "p i" '("invalidate project cache" . projectile-invalidate-cache) + "p k" '("kill project buffers" . projectile-kill-buffers) + "p o" '("find other file" . projectile-find-other-file) + "p p" '("switch project" . projectile-switch-project) + "p r" '("find recent project files" . projectile-recentf) + "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 +** 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 + (defun ha--persp-label (num names) + "Return string of numbered elements. NUM is the starting + number and NAMES is a list of strings." + (when names + (concat + (format " %d: %s" ; Shame that the following doesn't work: + num ; (propertize (number-to-string num) :foreground "#00a0") + (car names)) ; Nor does surrounding the number with underbars. + (ha--persp-label (1+ num) (cdr names))))) + + (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 + +Build the hydra as well as configure the =perspective= project. + +#+BEGIN_SRC emacs-lisp + (use-package perspective + :custom + (persp-modestring-short t) + (persp-sort 'name) + (persp-show-modestring t) + + :config + (persp-mode +1) + (defhydra hydra-workspace-leader (:color blue :hint nil) " + Workspaces- %s(ha-persp-labels) + _n_: new project _r_: rename _a_: add buffer _l_: load worksp + _]_: next worksp _d_: delete _b_: goto buffer _s_: save worksp + _[_: previous _W_: init all _k_: remove buffer + " + ("TAB" persp-switch) + ("1" (persp-switch-by-number 1)) + ("2" (persp-switch-by-number 2)) + ("3" (persp-switch-by-number 3)) + ("4" (persp-switch-by-number 4)) + ("5" (persp-switch-by-number 5)) + ("6" (persp-switch-by-number 6)) + ("7" (persp-switch-by-number 7)) + ("8" (persp-switch-by-number 8)) + ("9" (persp-switch-by-number 9)) + ("0" (persp-switch-by-number 0)) + ("n" ha-project-persp) + ("]" persp-next :color pink) + ("[" persp-prev :color pink) + ("r" persp-rename) + ("d" persp-kill) + ("W" ha-workspace-initialize) + ("a" persp-add-buffer) + ("b" persp-switch-to-buffer) + ("k" persp-remove-buffer) + ("K" persp-kill-buffer) + ("s" persp-state-save) + ("l" persp-state-load) + ("q" nil) + ("C-g" nil)) + + (ha-leader "TAB" '("workspaces" . hydra-workspace-leader/body))) +#+END_SRC +*** Predefined Workspaces +First step is to get rid of the /recent/ feature, as I don't really use that. +#+BEGIN_SRC emacs-lisp :tangle no +(recentf-mode -1) +(remove-hook 'kill-emacs-hook 'recentf-cleanup) +(remove-hook 'kill-emacs-hook 'save-place-kill-emacs-hook) +(remove-hook 'kill-emacs-hook 'savehist-autosave) +#+END_SRC + +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 +(defvar ha-workspace-projects-personal + '(("projects" "~/projects" ("breathe.org" "tasks.org")) + ("personal" "~/personal" ("general.org")) + ("technical" "~/technical" ("ansible.org")) + ("hamacs" "~/other/hamacs" ("README.org" "ha-config.org")) + ("rpg" "~/Dropbox/org/rpg" ("workdavians-dragon-heist.org" "dragon-heist.org")) + ("dm-work" "~/Dropbox/org/rpg-dm" ("README-mythic.org" "rpgdm.el"))) + "List of default projects with a name.") +#+END_SRC + +Given a list of information about project-workspaces, can we just create them all? + +#+BEGIN_SRC emacs-lisp +(defun ha-workspace-initialize (&optional projects) + "Precreate workspace projects from a PROJECTS list. +Each entry in the list is a list containing: + - name (as a string) + - project root directory + - a optional list of files to display" + (interactive) + (unless projects + (setq projects ha-workspace-projects-personal)) + + (dolist (project projects) + (-let (((name root files) project)) + (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. + +#+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 +PROJECT. If FILES aren't specified, then see if there is a +README. Otherwise, pull up a dired." + (interactive (list (projectile-completing-read "Project: " projectile-known-projects))) + (when (f-directory-p project) + (unless name + (setq name (f-filename project))) + (persp-switch name) + + ;; Unclear if the following is actually necessary. + (ignore-errors + (projectile-add-known-project root) + (projectile-switch-project-by-name root)) + + ;; To pin a project in projectile to the perspective, we need to load a file + ;; from that project. The README will do, or at least, the dired of it. + (if files + (ha--project-show-files project files) + (if-let ((readme (f-join project "README*"))) + (find-file readme 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. + +#+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 + long)." + (message "Loading files from %s ... %s" root files) + (let* ((file (car files)) + (more (cdr files)) + (filename (format "%s/%s" root file))) + (find-file filename) + (when more + (split-window-horizontally) + (ha--project-show-files root more)))) +#+END_SRC +* Applications +Can we really 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 + (use-package magit + :config + (ha-leader + "g" '(:ignore t :which-key "git") + "g /" '("Magit dispatch" . magit-dispatch) + "g ." '("Magit file dispatch" . magit-file-dispatch) + "g b" '("Magit switch branch" . magit-branch-checkout) + + "g g" '("Magit status" . magit-status) + "g s" '("Magit status here" . magit-status-here) + "g D" '("Magit file delete" . magit-file-delete) + "g B" '("Magit blame" . magit-blame-addition) + "g C" '("Magit clone" . magit-clone) + "g F" '("Magit fetch" . magit-fetch) + "g L" '("Magit buffer log" . magit-log-buffer-file) + "g R" '("Revert file" . vc-revert) + "g S" '("Git stage file" . magit-stage-file) + "g U" '("Git unstage file" . magit-unstage-file) + + "g f" '(:ignore t :which-key "find") + "g f f" '("Find file" . magit-find-file) + "g f g" '("Find gitconfig file" . magit-find-git-config-file) + "g f c" '("Find commit" . magit-show-commit) + + "g l" '(:ignore t :which-key "list") + "g l r" '("List repositories" . magit-list-repositories) + "g l s" '("List submodules" . magit-list-submodules) + + "g o" '(:ignore t :which-key "open") + + "g c" '(:ignore t :which-key "create") + "g c r" '("Initialize repo" . magit-init) + "g c R" '("Clone repo" . magit-clone) + "g c c" '("Commit" . magit-commit-create) + "g c f" '("Fixup" . magit-commit-fixup) + "g c b" '("Branch" . magit-branch-and-checkout))) +#+END_SRC + +Let's extend Magit with [[https://github.com/magit/forge][Magit Forge]] for working with Github and Gitlab: +#+BEGIN_SRC emacs-lisp + (use-package forge + :after magit + :config + (ha-leader + "g '" '("Forge dispatch" . forge-dispatch) + "g f i" '("Find issue" . forge-visit-issue) + "g f p" '("Find pull request" . forge-visit-pullreq) + + "g l i" '("List issues" . forge-list-issues) + "g l p" '("List pull requests" . forge-list-pullreqs) + "g l n" '("List notifications" . forge-list-notifications) + + "g o r" '("Browse remote" . forge-browse-remote) + "g o c" '("Browse commit" . forge-browse-commit) + "g o i" '("Browse an issue" . forge-browse-issue) + "g o p" '("Browse a pull request" . forge-browse-pullreq) + "g o i" '("Browse issues" . forge-browse-issues) + "g o P" '("Browse pull requests" . forge-browse-pullreqs) + + "g c i" '("Issue" . forge-create-issue) + "g c p" '("Pull request" . forge-create-pullreq))) +#+END_SRC + +The [[https://github.com/emacsmirror/git-timemachine][git-timemachine]] project is cool: +#+BEGIN_SRC emacs-lisp +(use-package git-timemachine + :config + (ha-leader "g t" '("git timemachine" . git-timemachine))) +#+END_SRC + +Using the [[https://github.com/emacsmirror/gist][gist package]] to write code snippets on either [[https://gist.github.com/][Github]]: +#+BEGIN_SRC emacs-lisp + (use-package gist + :config + (ha-leader + "g G" '(:ignore t :which-key "gists") + "g l g" '("gists" . gist-list) + "g G l" '("list" . gist-list) ; Lists your gists in a new buffer. + "g G r" '("region" . gist-region) ; Copies Gist URL into the kill ring. + "g G R" '("private region" . gist-region-private) ; Explicitly create a private gist. + "g G b" '("buffer" . gist-buffer) ; Copies Gist URL into the kill ring. + "g G B" '("private buffer" . gist-buffer-private) ; Explicitly create a private gist. + "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 +#+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 +(use-package eww + :init + (setq browse-url-browser-function 'eww-browse-url + browse-url-secondary-browser-function 'browse-url-default-browser + eww-browse-url-new-window-is-tab nil + shr-use-colors nil + shr-use-fonts t ; I go back and forth on this one + ; shr-discard-aria-hidden t + shr-bullet "• " + shr-inhibit-images nil ; Gotta see the images + ; shr-blocked-images '(svg) + ; shr-folding-mode nil + url-privacy-level '(email)) + + :config + (define-key eww-mode-map (kbd "L") #'eww-list-bookmarks) + (define-key eww-buffers-mode-map (kbd "q") #'eww-bookmark-kill) + (define-key eww-bookmark-mode-map (kbd "q") #'eww-bookmark-kill) + + (use-package ace-link + :config + (ace-link-setup-default))) +#+END_SRC +** Feed Reader + +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 +(setq rmh-elfeed-org-files (list (f-join hamacs-source-dir "my-feeds.org"))) +#+END_SRC + +While I would like to share the /status/ of my reads, so ... + +#+BEGIN_SRC emacs-lisp +(use-package elfeed + :config + (setq elfeed-db-directory "~/dropbox/.elfeed/") + (evil-define-key 'normal elfeed-show-mode-map (kbd "q") 'delete-window) + (evil-define-key 'normal elfeed-search-mode-map (kbd "r") 'ha/elfeed-tag-unread) + (evil-define-key 'normal elfeed-search-mode-map (kbd "R") 'elfeed-search-update--force) + + (use-package elfeed-org + :config + (elfeed-org))) + +(defun ha/elfeed-tag-unread () + (interactive) + (elfeed-search-untag-all 'unread) + (elfeed-search-update)) +#+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 +(defun bjm/elfeed-show-visit-gui () + "Wrapper for elfeed-show-visit to use gui browser instead of eww" + (interactive) + (let ((browse-url-generic-program "/usr/bin/open")) + (elfeed-show-visit t))) + +(define-key elfeed-show-mode-map (kbd "B") 'bjm/elfeed-show-visit-gui) +#+END_SRC + +** VTerm + +I'm not giving up on Eshell, but I am playing around with [[https://github.com/akermu/emacs-libvterm][vterm]], and it is pretty good, but I use it primarily as a more reliable approach to [[file:ha-remoting.org][a remote shell]]. + +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 +(use-package vterm + :init + (setq vterm-shell "/usr/local/bin/fish") + ;; Granted, I seldom pop out to the shell except during code demonstrations, + ;; but I like how C-p/C-n jumps up to each prompt entry using this setting + ;; that works with my prompt: + (setq vterm-use-vterm-prompt-detection-method nil + term-prompt-regexp "^.* $ ") + :config + (dolist (k '("" "")) + (define-key vterm-mode-map (kbd k) + (lambda () (interactive) (vterm-send-key (kbd "C-w"))))) + + (advice-add 'vterm-copy-mode :after 'evil-normal-state)) +#+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. + +*Note:* To exit the copy-mode (and copy the selected text to the clipboard), hit ~Return~. + +Hrm. Seems that I might want a function to copy the output of the last command to a register, or even an org-capture... +** Fish Shell +#+BEGIN_SRC emacs-lisp +(use-package fish-mode + :mode ("\\.fish" . fish-mode) + :hook (fish-mode . (lambda () (add-hook 'before-save-hook 'fish_indent-before-save)))) +#+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-config) +;;; ha-config.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 configuring Emacs. + +#+PROPERTY: header-args:sh :tangle no +#+PROPERTY: header-args:emacs-lisp :tangle yes +#+PROPERTY: header-args :results none :eval no-export :comments no + +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil +#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil +#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js diff --git a/ha-display.org b/ha-display.org new file mode 100644 index 0000000..9c581f7 --- /dev/null +++ b/ha-display.org @@ -0,0 +1,267 @@ +#+TITLE: Emacs Graphical Display Configuration +#+AUTHOR: Howard X. Abrams +#+EMAIL: howard.abrams@gmail.com +#+DATE: 2020-09-10 +#+FILETAGS: :emacs: + +A literate programming file to configure the Emacs UI. +# *Note:* After each change, /tangle it/ to the source destination with ~C-c C-v t~. + +#+BEGIN_SRC emacs-lisp :exports none +;;; ha-display.org --- A literate programming file to configure the Emacs UI. -*- lexical-binding: t; -*- +;; +;; Copyright (C) 2020 Howard X. Abrams +;; +;; Author: Howard X. Abrams +;; Maintainer: Howard X. Abrams +;; Created: September 10, 2020 +;; +;; This file is not part of GNU Emacs. +;; +;; *NB:* Do not edit this file. Instead, edit the original literate file at: +;; ~/other/hamacs/ha-display.org +;; Using `find-file-at-point', and tangle the file to recreate this one . +;; +;;; Code: +#+END_SRC +* Dashboard +The [[https://github.com/emacs-dashboard/emacs-dashboard][emacs-dashboard]] project makes a nicer startup screen. +#+BEGIN_SRC emacs-lisp + (use-package dashboard + :init + (setq dashboard-banner-logo-title "Welcome to Emacs" + dashboard-startup-banner "~/other/hamacs/support/levitating-gnu.png" + dashboard-center-content t + dashboard-set-init-info t + dashboard-projects-switch-function 'projectile-persp-switch-project + dashboard-items '((projects . 5) + ;; (agenda . 5) + (bookmarks . 5))) + :config + (dashboard-setup-startup-hook) + + (setq dashboard-footer-messages (list (ha--dad-joke)))) +#+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 + (use-package doom-modeline + :init + (setq doom-modeline-minor-modes nil + doom-modeline-buffer-encoding nil + doom-modeline-percent-position nil) + :config + (doom-modeline-mode +1)) + + (use-package doom-themes) +#+END_SRC +* Themes +One does get used to a particular collection of colors. Mine is Tomorrow: +#+BEGIN_SRC emacs-lisp +(use-package color-theme-sanityinc-tomorrow) +#+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 +(defun laptop-inside () + (interactive) + (load-theme 'sanityinc-tomorrow-night t) + (set-face-attribute 'region nil :background "#000096") + ;; font-lock-comment-face #8a8b8a + (set-face-attribute 'mode-line nil :background "black") + (set-face-attribute 'mode-line-inactive nil :background "#333333")) +#+END_SRC + +But, when feeling adventurous, I /sometimes/ take my laptop outside: + +#+BEGIN_SRC emacs-lisp +(defun laptop-in-the-sun () + (interactive) + (load-theme 'sanityinc-tomorrow-day t) + (set-face-attribute 'region nil :background "orange1") + ;; font-lock-comment-face #8a8b8a + (set-face-attribute 'mode-line nil :background "#cccccc") + (set-face-attribute 'mode-line-inactive nil :background "#888888")) +#+END_SRC + +Oh, and turn off the line highlighting: + +#+BEGIN_SRC emacs-lisp +(global-hl-line-mode -1) +#+END_SRC + +And of course, the default is /inside/ where it is dark and safe: + +#+BEGIN_SRC emacs-lisp +(laptop-inside) +#+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 +(add-to-list 'initial-frame-alist '(fullscreen . maximized)) +#+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. +** Choosing a Font +I stole the following idea from [[https://protesilaos.com/dotemacs/#h:9035a1ed-e988-4731-89a5-0d9e302c3dea][Protesilaos Stavrou's dotfile configuration]], and the following should minimally be /readable/: + | Similarities | Regular | + |--------------+----------------------------| + | ()[]{}<>«»‹› | ABCDEFGHIJKLMNOPQRSTUVWXYZ | + | 6bB8& | abcdefghijklmnopqrstuvwxyz | + | 0ODdoaoOQGC | 0123456789 | + | I1tilIJL | ~!@#$%^&*+ | + | !¡ij | `'"‘’“”.,;:… | + | 5$§SsS5 | ()[]{}—-_=<>/\ | + | 17ZzZ2 | ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ | + | 9gqpG6 | αβγδεζηθικλμνξοπρστυφχψω | + | hnmMN | | + | uvvwWuuwvy | | + | x×X | #include // <= quickly. | + | .,·°% | int main(int argc, char **argv) { | + | ¡!¿? | long il1[]={1-2/3.4,5+6==7/8}; | + | :; | int OxFaced=0xBAD||"[{(CQUINE"; | + | `''"‘’“” | unsigned O0,l1,Z2,S5,G6,B8__XY; | + | —-~≈=≠+*_ | printf("@$Hamburgefo%c`",'\n'); | + | …⋯ | return ~7&8^9?0:l1|!"j->k+=*w"; | + | ... | | + + +The following is from [[https://source-foundry.github.io/Hack/font-specimen.html][Hack's website]]: +#+BEGIN_SRC c +// The four boxing wizards jump +#include // <= quickly. +int main(int argc, char **argv) { + long il1[]={1-2/3.4,5+6==7/8}; + int OxFaced=0xBAD||"[{(CQUINE"; + unsigned O0,l1,Z2,S5,G6,B8__XY; + printf("@$Hamburgefo%c`",'\n'); + return ~7&8^9?0:l1|!"j->k+=*w"; +} +#+END_SRC + +To install a font, I use the following command on my Mac: +#+BEGIN_SRC sh +brew tap homebrew/cask-fonts +brew install --cask font-hack-nerd-font +#+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 + (defvar ha/fixed-font + (when window-system + (cond ((x-list-fonts "Hack Nerd Font") "Hack Nerd Font") + ((x-list-fonts "Cousine Nerd Font") "Cousine Nerd Font") + ((x-list-fonts "Iosevka Nerd Font") "Iosevka Nerd Font") + ((x-list-fonts "Iosevka") "Iosevka") + ((x-list-fonts "FantasqueSansMono Nerd Font") "FantasqueSansMono Nerd Font") + ((x-list-fonts "Monoid Nerd Font") "Monoid Nerd Font") + ((x-list-fonts "Hasklig") "Hasklig") + ((x-list-fonts "Cascadia Code PL") "Cascadia Code PL") + ((x-list-fonts "Source Code Pro") "Source Code Pro") + ((x-list-fonts "Anonymous Pro") "Anonymous Pro") + (t "monospaced"))) + "My fixed width font based on what is installed, `nil' if not defined.") +#+END_SRC + +Force something as well: +#+BEGIN_SRC emacs-lisp :tangle no +(setq ha/fixed-font "Hack Nerd Font") +#+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 +(defvar ha/variable-font + (when window-system + (cond ((x-list-fonts "Overpass") "Overpass") + ((x-list-fonts "Source Sans Pro") "Source Sans Pro") + ((x-list-fonts "Lucida Grande") "Lucida Grande") + ((x-list-fonts "Verdana") "Verdana") + ((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 + +Simple function that gives me the font information based on the size I need. +This calls =set-frame-font=, but also sets the monospaced font for org code blocks. + +#+BEGIN_SRC emacs-lisp :results none +(defun ha/set-favorite-font-size (size) + (let ((fav-font (format "%s-%d" ha/fixed-font size))) + (set-frame-font fav-font nil t) + + ;; When using variable-pitch in org, we need to specifically set the + ;; fixed-pitch as my default fixed-pitched font. + (custom-theme-set-faces + 'user + `(variable-pitch ((t (:family ,ha/variable-font :slant normal :weight normal :height 1.1 :width normal)))) + `(fixed-pitch ((t (:family ,ha/fixed-font :slant normal :weight normal :height 1.0 :width normal))))))) +#+END_SRC + + Define a few /interactive/ functions to quickly adjusting the font size based on my computing scenario: + +#+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) + (ha/set-favorite-font-size 13)) + +(defun ha/linux-monitor-fontsize () + "Quickly set reset my font size when I connect my laptop to a monitor on Linux." + (interactive) + (ha/set-favorite-font-size 12)) + +(defun ha/mac-laptop-fontsize () + "Quickly set reset my font size when I disconnect my laptop to a monitor from a Mac." + (interactive) + (ha/set-favorite-font-size 32)) + +(defun ha/linux-laptop-fontsize () + "Quickly set reset my font size when I disconnect my laptop to a monitor from Linux." + (interactive) + (ha/set-favorite-font-size 14)) +#+END_SRC + +Which font to choose? + +#+BEGIN_SRC emacs-lisp +(if (eq system-type 'gnu/linux) + (ha/linux-laptop-fontsize) + (ha/mac-monitor-fontsize)) +#+END_SRC +* 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) + +(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 + +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 to configure the Emacs UI. + +#+PROPERTY: header-args:sh :tangle no +#+PROPERTY: header-args:emacs-lisp :tangle yes +#+PROPERTY: header-args :results none :eval no-export :comments no :mkdirp yes + +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil +#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil +#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js diff --git a/ha-org-word-processor.org b/ha-org-word-processor.org new file mode 100644 index 0000000..7745330 --- /dev/null +++ b/ha-org-word-processor.org @@ -0,0 +1,204 @@ +#+TITLE: Org As A Word Processor +#+AUTHOR: Howard X. Abrams +#+EMAIL: howard.abrams@gmail.com +#+DATE: 2020-09-10 +#+FILETAGS: :emacs: + +A literate programming file for making Org file more readable. +# *Note:* After each change, /tangle it/ to the source destination with ~C-c C-v t~. + +#+BEGIN_SRC emacs-lisp :exports none +;;; ha-org-word-processor.org --- A literate programming file for making Org file more readable. -*- lexical-binding: t; -*- +;; +;; Copyright (C) 2020 Howard X. Abrams +;; +;; Author: Howard X. Abrams +;; Maintainer: Howard X. Abrams +;; Created: September 10, 2020 +;; +;; This file is not part of GNU Emacs. +;; +;; *NB:* Do not edit this file. Instead, edit the original literate file at: +;; ~/other/hamacs/ha-org-word-processor.org +;; Using `find-file-at-point', and tangle the file to recreate this one . +;; +;;; Code: +#+END_SRC +* Introduction +I like having org-mode files look more like editing in a word processor than having it look like programming code. But that is just me. +* General Org Settings +Since I use ellipsis in my writing... I like to /change/ how org renders a collapsed heading. + +#+BEGIN_SRC emacs-lisp + (setq org-pretty-entities t + org-ellipsis "⤵" ; …, ➡, ⚡, ▼, ↴, , ∞, ⬎, ⤷, ⤵ + org-agenda-breadcrumbs-separator " ❱ " + org-src-fontify-natively t ;; Pretty code blocks + org-hide-emphasis-markers t) +#+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 +(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 + :foreground default-color :weight 'bold :font ha/variable-font)))) +#+END_SRC + +Next, we just need to change the header sizes: + +#+BEGIN_SRC emacs-lisp +(when window-system + (set-face-attribute 'org-level-1 nil :height 2.2 :inherit 'default) + (set-face-attribute 'org-level-2 nil :height 1.8 :inherit 'default) + (set-face-attribute 'org-level-3 nil :height 1.4 :inherit 'default) + (set-face-attribute 'org-level-4 nil :height 1.1 :inherit 'default)) +#+END_SRC +* Superstar +Now that headers are noticeable, I have no reason to see a number of asterisks. Once I used the [[https://github.com/sabof/org-bullets][org-bullets]] package, but believe we've replaced it with [[https://github.com/integral-dw/org-superstar-mode][org-superstar-mode]], so the following is an improvement, especially with the sub-bullets: + +#+BEGIN_SRC emacs-lisp +(use-package org-superstar + :init + (add-hook 'org-mode 'org-superstar-mode) + (setq org-superstar-headline-bullets-list '("▶") + org-superstar-special-todo-items t + org-superstar-todo-bullet-alist t + org-superstar-prettify-item-bullets t + org-superstar-item-bullet-alist '((42 . "⊙") ; * + (43 . "⁍") ; + + (45 . "•")))) +#+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: + +#+BEGIN_SRC emacs-lisp +(setq org-list-demote-modify-bullet '(("*" . "+") ("+" . "-") ("-" . "-") + ("1." . "a.") ("a." . "1."))) +#+END_SRC + +Since the following code does not work like I would have expected: +#+BEGIN_SRC emacs-lisp :tangle no +(setq org-hide-leading-stars t) +#+END_SRC + +I add a hook to standard Org, and since this is a Lisp-2, I can get away with: +#+BEGIN_SRC emacs-lisp +(defun org-hide-leading-stars () + (let* ((keyword + `(("^\\(\\*+ \\)\\s-*\\S-" ; Do not hide empty headings! + (1 (put-text-property (match-beginning 1) (match-end 1) 'invisible t) + nil))))) + (font-lock-add-keywords nil keyword))) + + (add-hook 'org-mode-hook 'org-hide-leading-stars) +#+END_SRC +* Checkboxes +According to an idea by [[https://jft.home.blog/2019/07/17/use-unicode-symbol-to-display-org-mode-checkboxes/][Huy Trần]], we can prettify the list checkboxes as well: + +#+BEGIN_SRC emacs-lisp +(defun ha/org-prettify-checkboxes () + "Beautify Org Checkbox Symbol" + (push '("[ ]" . "☐") prettify-symbols-alist) + (push '("[X]" . "☒") prettify-symbols-alist) + (push '("[-]" . "☐-") prettify-symbols-alist) + (prettify-symbols-mode)) +#+END_SRC + +And now we can attach it to a newly loaded org files: + +#+BEGIN_SRC emacs-lisp +(add-hook 'org-mode-hook 'ha/org-prettify-checkboxes) +#+END_SRC + +To make it more distinguishable, he also 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.") + +(font-lock-add-keywords + 'org-mode + `(("^[ \t]*\\(?:[-+*]\\|[0-9]+[).]\\)[ \t]+\\(\\(?:\\[@\\(?:start:\\)?[0-9]+\\][ \t]*\\)?\\[\\(?:X\\|\\([0-9]+\\)/\\2\\)\\][^\n]*\n\\)" + 1 'org-checkbox-done-text prepend)) + 'append) +#+END_SRC +* Padding +The [[https://github.com/TonCherAmi/org-padding][org-padding]] project looks places extra space before and after headers and blocks (essentially leading), to create a more word-processor-y experience. Great idea, however, I have spent a lot of extra time entering blank lines before and after my headers and blocks: + +#+BEGIN_SRC emacs-lisp +(use-package org-padding + :straight (org-padding :type git :host github :repo "TonCherAmi/org-padding") + :hook + (org-mode . org-padding-mode) + :config + (setq org-padding-block-begin-line-padding '(0.5 . 0.3) + org-padding-block-end-line-padding '(0.1 . 0.5) + org-padding-heading-padding-alist + '((4.0 . 1.5) (3.0 . 0.5) (3.0 . 0.5) (3.0 . 0.5) (2.5 . 0.5) (2.0 . 0.5) (1.5 . 0.5) (0.5 . 0.5)))) +#+END_SRC +However, I'm just going to have to write a function to clean this. +#+BEGIN_SRC emacs-lisp +(defun ha/remove-superfluous-org-padding () + (interactive) + (goto-char (point-min)) + (ha/remove-org-header-padding) + (goto-char (point-min)) + (ha/remove-org-block-padding)) + +(defun ha/remove-org-header-padding () + ;; (goto-char (point-min)) + (while (re-search-forward (rx (optional bol (zero-or-more space) eol "\n") + (group bol (one-or-more "*") (one-or-more space) (one-or-more any) "\n") + (optional bol (zero-or-more space) eol "\n")) nil t) + (replace-match (match-string 1) nil :no-error))) + +(defun ha/remove-org-block-padding () + ;; (goto-char (point-min)) + (while (re-search-forward (rx (optional bol (zero-or-more space) eol "\n") + (group bol (zero-or-more space) "#+BEGIN" (one-or-more any) eol "\n" + (zero-or-more (group bol (zero-or-more any) eol "\n")) + 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 +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 :results silent +(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 +The [[https://github.com/takaxp/org-tree-slide][org-tree-slide]] still seems to be the best presentation tool for Org files, but I really need to issue a pull request to fix a few warnings. +#+BEGIN_SRC emacs-lisp +(use-package org-tree-slide + :init + (setq org-tree-slide-skip-outline-level 4) + :config + (org-tree-slide-simple-profile)) +#+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-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. + +#+PROPERTY: header-args:sh :tangle no +#+PROPERTY: header-args:emacs-lisp :tangle yes +#+PROPERTY: header-args :results none :eval no-export :comments no + +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil +#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil +#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js diff --git a/ha-org.org b/ha-org.org new file mode 100644 index 0000000..d5ce021 --- /dev/null +++ b/ha-org.org @@ -0,0 +1,332 @@ +#+TITLE: General Org-Mode Configuration +#+AUTHOR: Howard X. Abrams +#+EMAIL: howard.abrams@gmail.com +#+DATE: 2020-09-18 +#+FILETAGS: :emacs: + +A literate programming file for configuring org-mode and those files. +# *Note:* After each change, /tangle it/ to the source destination with ~C-c C-v t~. + +#+BEGIN_SRC emacs-lisp :exports none +;; +;; Copyright (C) 2020 Howard X. Abrams +;; +;; Author: Howard X. Abrams +;; Maintainer: Howard X. Abrams +;; Created: September 18, 2020 +;; +;; This file is not part of GNU Emacs. +;; +;; *NB:* Do not edit this file. Instead, edit the original literate file at: +;; /home/howard/other/hamacs/ha-org.org +;; And tangle the file to recreate this one. +;; +;;; Code: +(use-package org + :mode ("\\.org" . org-mode) ; Addresses an odd warning + :init +#+END_SRC +* Initialization Section +Org is an important part of my Emacs world, and with a lot of customization (even though Spacemacs and Doom do a good job getting things started). + +#+BEGIN_SRC emacs-lisp + (setq org-return-follows-link t + org-startup-indented t + ;; Speed Commands: If point is at the beginning of a headline or code + ;; block in org-mode, single keys do fun things. See + ;; org-speed-command-help for details (or hit the ? key at a headline). + org-use-speed-commands t + + org-directory "~/personal" + org-default-notes-file "~/personal/general-notes.txt" + + org-enforce-todo-dependencies t ; Can't close a task until subtasks are done + org-agenda-dim-blocked-tasks t + org-log-done 'time + + org-completion-use-ido t + org-outline-path-complete-in-steps nil + org-src-tab-acts-natively t + org-agenda-span 'day ; Default is 'week + org-confirm-babel-evaluate nil) +#+END_SRC +Overcoming a bug: +#+BEGIN_SRC emacs-lisp +(defun org-clocking-buffer (&rest ignored)) +#+END_SRC +* Configuration Section +The following sections assume that org has been loaded, as this begin the configuration section: +#+BEGIN_SRC emacs-lisp +:config +#+END_SRC +I pretend that my org files are word processing files that wrap automatically: +#+BEGIN_SRC emacs-lisp +(add-hook 'org-mode-hook #'visual-line-mode) +#+END_SRC +Files that end in =.txt= are still org files to me: +#+BEGIN_SRC emacs-lisp +(add-to-list 'auto-mode-alist '("\\.txt\\'" . org-mode)) +#+END_SRC +Many of the files that I edit close some, but not all, of the headers using a file variable. Let's allow that to not insist that I need to approve that: + +#+BEGIN_SRC emacs-lisp +(add-to-list 'safe-local-variable-values '(org-content . 2)) +#+END_SRC +** 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 only if we are at the end of the line. + +#+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', + otherwise, just call `org-return' as usual." + (interactive) + (if (eolp) + (ha/org-special-return) + (org-return))) +#+END_SRC + +And bind it to the Return key: +#+BEGIN_SRC emacs-lisp +(define-key org-mode-map (kbd "RET") #'ha/org-return) +#+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. + - On a link, call =org-return= in order to open it. + - On a header? Let's create a new header (or maybe not). + - In a table? Let's create a new row. + - If we are really in a list, we can create a new item. + +I really should break this function into smaller bits ... + +#+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. +Use a prefix arg to get regular RET." + (interactive "P") + (if ignore + (org-return) + (cond + ;; Open links like usual + ((eq 'link (car (org-element-context))) + (org-return)) + + ((and (org-really-in-item-p) (not (bolp))) + (if (org-element-property :contents-begin (org-line-element-context)) + (progn + (end-of-line) + (org-insert-item)) + (delete-region (line-beginning-position) (line-end-position)))) + + ;; ((org-at-heading-p) + ;; (if (string= "" (org-element-property :title (org-element-context))) + ;; (delete-region (line-beginning-position) (line-end-position)) + ;; (org-insert-heading-after-current))) + + ((org-at-table-p) + (if (-any? + (lambda (x) (not (string= "" x))) + (nth + (- (org-table-current-dline) 1) + (org-table-to-lisp))) + (org-return) + ;; empty row + (beginning-of-line) + (setf (buffer-substring + (line-beginning-position) (line-end-position)) "") + (org-return))) + + (t + (org-return))))) +#+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 only one blank line. + +#+BEGIN_SRC emacs-lisp +(defun org-really-in-item-p () + "Similar to `org-in-item-p', however, this works around an +issue where the point could actually be in some =code= words, but +still be on an item element." + (save-excursion + (let ((location (org-element-property :contents-begin (org-line-element-context)))) + (when location + (goto-char location)) + (org-in-item-p)))) +#+END_SRC + +The org API allows getting the context associated with /current element/. However, this could be a line-level symbol, like paragraph or =list-item= only if 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 +(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 +** Unfill Paragraph +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 +(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 +** Tasks +I need to add a /blocked/ state: + +#+BEGIN_SRC emacs-lisp +(setq org-todo-keywords '((sequence "TODO(t)" "DOING(g)" "|" "DONE(d)" ) + (sequence "BLOCKED(b)" "|" "CANCELLED(c)"))) +#+END_SRC + +And I would like to have cute little icons for those states: + +#+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 + (1 (progn (compose-region (match-beginning 1) (match-end 1) "⚑") nil))) + ("^\\*+ \\(DOING\\) " + (1 (progn (compose-region (match-beginning 1) (match-end 1) "⚐") nil))) + ("^\\*+ \\(CANCELED\\) " + (1 (progn (compose-region (match-beginning 1) (match-end 1) "✘") nil))) + ("^\\*+ \\(BLOCKED\\) " + (1 (progn (compose-region (match-beginning 1) (match-end 1) "✋") nil))) + ("^\\*+ \\(DONE\\) " + (1 (progn (compose-region (match-beginning 1) (match-end 1) "✔") nil))) + ;; Here is my approach for quickly making the + ;; initial asterisks for listing items and whatnot, + ;; appear as Unicode bullets (without actually + ;; affecting the text file or the behavior). + ("^ +\\([-*]\\) " + (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) "•"))))))) +#+END_SRC + +** Meetings +I've notice that while I really like taking notes in a meeting, I don't always like the multiple windows I have opened, so I created this function that I can easily call to eliminate distractions during a meeting. + +#+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." + (interactive) + (outline-mark-subtree) ; Select org-mode section + (narrow-to-region (region-beginning) (region-end)) ; Only show that region + (deactivate-mark) + (delete-other-windows) ; remove other windows + (text-scale-set 2) ; readable by others + (fringe-mode 0) + (message "When finished taking your notes, run meeting-done.")) +#+END_SRC + +Of course, I need an 'undo' feature when the meeting is over... + +#+BEGIN_SRC emacs-lisp +(defun meeting-done () + "Attempt to 'undo' the effects of taking meeting notes." + (interactive) + (widen) ; Opposite of narrow-to-region + (text-scale-set 0) ; Reset the font size increase + (fringe-mode 1) + (winner-undo)) ; Put the windows back in place +#+END_SRC +** Misc +*** Babel Blocks +Whenever I edit Emacs Lisp blocks from my tangle-able configuration files, I get a lot of superfluous warnings. Let's turn them off. + +#+BEGIN_SRC emacs-lisp +(defun disable-fylcheck-in-org-src-block () + (setq-local flycheck-disabled-checkers '(emacs-lisp-checkdoc))) + +(add-hook 'org-src-mode-hook 'disable-fylcheck-in-org-src-block) +#+END_SRC +*** Next Image +When I create images or other artifacts that I consider /part/ of the org document, I want to have them based on the org file, but with a prepended number. Keeping track of what numbers are now free is difficult, so for a /default/ let's figure it out: + +#+BEGIN_SRC emacs-lisp +(defun ha/org-next-image-number (&optional prefix) + (when (null prefix) + (if (null (buffer-file-name)) + (setq prefix "cool-image") + (setq prefix (file-name-base (buffer-file-name))))) + + (save-excursion + (goto-char (point-min)) + (let ((largest 0) + (png-reg (rx (literal prefix) "-" (group (one-or-more digit)) (or ".png" ".svg")))) + (while (re-search-forward png-reg nil t) + (setq largest (max largest (string-to-number (match-string-no-properties 1))))) + (format "%s-%02d" prefix (1+ largest))))) +#+END_SRC +*** In a PlantUML Block +To make the snippets more context aware, this predicate + +#+BEGIN_SRC emacs-lisp +(defun ha/org-nested-in-plantuml-block () + "Predicate is true if point is inside a Plantuml Source code block in org-mode." + (equal "plantuml" + (plist-get (cadr (org-element-at-point)) :language))) +#+END_SRC +* Supporting Packages +At this point, we assume that the =use-package= for org is complete, so we can close it and allow other projects to be loaded: +#+BEGIN_SRC emacs-lisp +) +#+END_SRC +** Exporters +Need a few extra exporters: +#+BEGIN_SRC emacs-lisp :tangle no +(use-package ox-md) + +(use-package ox-confluence + :load-path "~/.doom.d/elisp") +#+END_SRC + +And Graphviz configuration using [[https://github.com/ppareit/graphviz-dot-mode][graphviz-dot-mode]]: +#+BEGIN_SRC emacs-lisp +(use-package graphviz-dot-mode + :mode "\\.dot\\'" + :init + (setq tab-width 4 + graphviz-dot-indent-width 2 + graphviz-dot-auto-indent-on-newline t + graphviz-dot-auto-indent-on-braces t + graphviz-dot-auto-indent-on-semi t)) +#+END_SRC +And we can install company support: +#+BEGIN_SRC emacs-lisp :tangle no +(use-package company-graphviz-dot) +#+END_SRC +** Writegood + +The [[https://github.com/bnbeckwith/writegood-mode][writegood-mode]] highlights passive and weasel words as typed. Shame it doesn't check for dangled prepositions. + +#+BEGIN_SRC emacs-lisp +(use-package writegood-mode + :hook ((org-mode . writegood-mode))) +#+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) +;;; ha-org.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 configuring org-mode and those files. + +#+PROPERTY: header-args:sh :tangle no +#+PROPERTY: header-args:emacs-lisp :tangle yes +#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes + +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil +#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil +#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js diff --git a/initialize b/initialize new file mode 100755 index 0000000..d82b253 --- /dev/null +++ b/initialize @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +HAMACS_DIR=$(cd "$(dirname "$0")"; pwd) +HAMACS_DEST=$HOME/.emacs.hamacs # A symlink to ~/.emacs.d + +mkdir -p $HAMACS_DEST +mkdir -p $HAMACS_DEST/elisp + +cat > $HAMACS_DEST/init.el < +;; Maintainer: `user-full-name` <`user-mail-address`> +;; Created: `(format-time-string "%B %e, %Y")` +;; +;; This file is not part of GNU Emacs. +;; +;; +;;; Code: + +$0 + +(provide '`(file-name-base (buffer-file-name)))`) +;;; `(file-name-base (buffer-file-name)))`.el ends here \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/ert-deftest b/snippets/emacs-lisp-mode/ert-deftest new file mode 100644 index 0000000..a454e5a --- /dev/null +++ b/snippets/emacs-lisp-mode/ert-deftest @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: ert-deftest +# key: edt +# -- +(ert-deftest $1-test () + (should (= $0))) \ No newline at end of file diff --git a/snippets/emacs-lisp-mode/npc b/snippets/emacs-lisp-mode/npc new file mode 100644 index 0000000..4cf3824 --- /dev/null +++ b/snippets/emacs-lisp-mode/npc @@ -0,0 +1,10 @@ +# -*- mode: snippet -*- +# name: npc +# key: npc +# -- +(defun rpgdm-npc--${1:$(replace-regexp-in-string " " "-" yas-text)} () + "Return string from a random $1." + (let ((roll (rpgdm--roll-die $2))) + (cond + ((<= roll $3) $4) + ((<= roll $5) $6))) \ No newline at end of file diff --git a/snippets/markdown-mode/h1 b/snippets/markdown-mode/h1 new file mode 100644 index 0000000..f383f46 --- /dev/null +++ b/snippets/markdown-mode/h1 @@ -0,0 +1,8 @@ +# -*- mode: snippet -*- +# name: header +# key: h1 +# -- +${1:Header} +${1:$(make-string (string-width yas-text) ?\=)} + +$0 \ No newline at end of file diff --git a/snippets/markdown-mode/h2 b/snippets/markdown-mode/h2 new file mode 100644 index 0000000..9b20d72 --- /dev/null +++ b/snippets/markdown-mode/h2 @@ -0,0 +1,8 @@ +# -*- mode: snippet -*- +# name: subheader +# key: h2 +# -- +${1:Header} +${1:$(make-string (string-width yas-text) ?\-)} + +$0 \ No newline at end of file diff --git a/snippets/mhtml-mode/div b/snippets/mhtml-mode/div new file mode 100644 index 0000000..2e42889 --- /dev/null +++ b/snippets/mhtml-mode/div @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: div +# key: div +# -- + + $0 + \ No newline at end of file diff --git a/snippets/org-journal-mode/__journal b/snippets/org-journal-mode/__journal new file mode 100644 index 0000000..94117ee --- /dev/null +++ b/snippets/org-journal-mode/__journal @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: __journal +# key: __journal +# -- +#+TITLE: Journal Entry- `(ha/journal-file-datestamp)` + +$0 diff --git a/snippets/org-mode/__hamacs b/snippets/org-mode/__hamacs new file mode 100644 index 0000000..a961be4 --- /dev/null +++ b/snippets/org-mode/__hamacs @@ -0,0 +1,62 @@ +# -*- mode: snippet -*- +# name: __hamacs +# key: __hamacs +# -- +#+TITLE: ${1:`(->> (buffer-file-name) + (file-name-base) + (s-split-words) + (--map (s-capitalize it)) + (s-join " "))`} +#+AUTHOR: `user-full-name` +#+EMAIL: `user-mail-address` +#+DATE: `(format-time-string "%Y-%m-%d")` +#+FILETAGS: :emacs: + +${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 + +* Introduction + +$0 + +* Technical Artifacts :noexport: + +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 + +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 + +#+PROPERTY: header-args:sh :tangle no +#+PROPERTY: header-args:emacs-lisp :tangle yes +#+PROPERTY: header-args :results none :eval no-export :comments no mkdirp yes + +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil date:nil +#+OPTIONS: skip:nil author:nil email:nil creator:nil timestamp:nil +#+INFOJS_OPT: view:nil toc:nil ltoc:t mouse:underline buttons:0 path:http://orgmode.org/org-info.js + +# Local Variables: +# eval: (add-hook 'after-save-hook #'org-babel-tangle t t) +# End: \ No newline at end of file diff --git a/snippets/org-mode/__mythic_rpg_overview b/snippets/org-mode/__mythic_rpg_overview new file mode 100644 index 0000000..aecf4a0 --- /dev/null +++ b/snippets/org-mode/__mythic_rpg_overview @@ -0,0 +1,24 @@ +# -*- mode: snippet -*- +# name: __mythic_rpg_overview +# key: mythic-rpg-overview +# -- +#+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: mythic rpg adventure + +Current Chaos Factor: 5 + +* Adventure Notes +$0 + +* Scenes +* Characters + - +* Threads + - + +# Local Variables: +# eval: (progn (load-library "rpgdm") (load-library "rpgdm-mythic") (rpgdm-tables-load)) +# End: \ No newline at end of file diff --git a/snippets/org-mode/__mythic_rpg_player b/snippets/org-mode/__mythic_rpg_player new file mode 100644 index 0000000..9ede5c9 --- /dev/null +++ b/snippets/org-mode/__mythic_rpg_player @@ -0,0 +1,28 @@ +# -*- mode: snippet -*- +# name: __mythic_rpg_player +# key: mythic-rpg-player +# -- +#+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: mythic rpg player pc character + +Favor Points: 50 + +* Character Summary +$0 +* Attributes + | Strength | | + | Agility | | + | Reflex | | + | IQ | | + | Intuition | | + | Willpower | | + | Toughness | | +* Abilities + | Ability | Rank | + |---------|------| + | | | +* Notes +* Strengthes and Weaknesses \ No newline at end of file diff --git a/snippets/org-mode/__mythic_rpg_resolution b/snippets/org-mode/__mythic_rpg_resolution new file mode 100644 index 0000000..fcd6a30 --- /dev/null +++ b/snippets/org-mode/__mythic_rpg_resolution @@ -0,0 +1,24 @@ +# -*- mode: snippet -*- +# name: __mythic_rpg_resolution +# key: mythic-rpg-resolution +# -- +#+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: mythic rpg resolution chart + +${0:Summary Description} + +*Acting Rank:* $2 + +*Difficulty Rank:* $3 + +*Modifiers:* + - $4 + +*Results:* + | Yes | | + | No | | + | Exceptional Yes | | + | Exceptional No | | diff --git a/snippets/org-mode/__mythic_rpg_scaling b/snippets/org-mode/__mythic_rpg_scaling new file mode 100644 index 0000000..29e7475 --- /dev/null +++ b/snippets/org-mode/__mythic_rpg_scaling @@ -0,0 +1,26 @@ +# -*- mode: snippet -*- +# name: __mythic_rpg_scaling +# key: mythic-rpg-scaling +# -- +#+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: mythic rpg scaling skill box + +${0:Example of...} + + | Description | Mythic Rank | + |-------------+---------------| + | | | + | | Miniscule | + | | Weak | + | | Low | + | | Below Average | + | $2 | Average | + | | Above Average | + | | High | + | | Exceptional | + | | Incredible | + | | Awesome | + | | | \ No newline at end of file diff --git a/snippets/org-mode/__sprint b/snippets/org-mode/__sprint new file mode 100644 index 0000000..e32df60 --- /dev/null +++ b/snippets/org-mode/__sprint @@ -0,0 +1,61 @@ +# -*- mode: snippet -*- +# name: sprint +# key: __sprint +# -- +#+TITLE: `(sprint-current-name)` +#+AUTHOR: `user-full-name` +#+EMAIL: `user-mail-address` +#+DATE: `(sprint-date-range)` +#+CATEGORY: sprint +#+FILETAGS: :work: + +* Work Issues + +$0 + +* Onboarding Work + +See [[file:Onboarding-Work.org][Onboard-Work]] for all details. + +* Distractions and Support + +Anything that doesn't fit the above goes here. + +* Meeting Notes :meeting: +* Scrum Status :status: + +* Sprint Demonstration +SCHEDULED: <`(sprint-date-from-start 12)`> DEADLINE: <`(sprint-date-from-start 13)`> + +* Sprint Retrospective +SCHEDULED: <`(sprint-date-from-start 14)`> + +* Notes for Next Sprint + +** Support Section + +We should always allow the display of this file for a demonstration. + +#+BEGIN_SRC elisp :results silent + (use-package demo-it + :load-path "~/Other/demo-it" + :config + (demo-it-create :advanced-mode :single-window + (demo-it-presentation (buffer-file-name) 3 :both "Sprint Demonstration") + (osx-browse-url-forwork "https://...") + (demo-it-load-fancy-file "~/work/wpc4/wpc/dashboards/wpc4/hypervisor.yml" + :line 116 133) + (demo-it-presentation-return-noadvance) + ;; ... + )) +#+END_SRC + +Have fun and start the show: =demo-it-start= and hit ~F5~ (or the ~Num~ key on Keyboardio). + +#+DESCRIPTION: Notes taken during Sprint #`(sprint-number)` +#+PROPERTY: header-args: :results drawer :tangle no :eval no-export :comments org +#+OPTIONS: num:nil toc:nil todo:nil tasks:nil tags:nil skip:nil author:nil email:nil creator:nil timestamp:nil ^:nil + +# Local Variables: +# eval: (org-content 2) +# End: diff --git a/snippets/org-mode/activity-diagram b/snippets/org-mode/activity-diagram new file mode 100644 index 0000000..7db746f --- /dev/null +++ b/snippets/org-mode/activity-diagram @@ -0,0 +1,18 @@ +# -*- mode: snippet -*- +# contributor: Howard Abrams +# name: activity-diagram +# key: activity +# condition: (ha/org-nested-in-plantuml-block) +# group: plantuml +# -- +' See details at https://plantuml.com/activity-diagram-betastart +start + +if (Graphviz installed?) then (yes) + :process all\ndiagrams; +else (no) + :process only + __sequence__ and __activity__ diagrams; +endif + +stop \ No newline at end of file diff --git a/snippets/org-mode/component-diagram b/snippets/org-mode/component-diagram new file mode 100644 index 0000000..25b0397 --- /dev/null +++ b/snippets/org-mode/component-diagram @@ -0,0 +1,34 @@ +# -*- mode: snippet -*- +# contributor: Howard Abrams +# name: component-diagram +# key: component +# condition: (ha/org-nested-in-plantuml-block) +# group: plantuml +# -- +' See details at https://plantuml.com/component-diagram +package "Some Group" { + HTTP - [First Component] + [Another Component] +} + +node "Other Groups" { + FTP - [Second Component] + [First Component] --> FTP +} + +cloud { + [Example 1] +} + +database "MySql" { + folder "This is my folder" { + [Folder 3] + } + frame "Foo" { + [Frame 4] + } +} + +[Another Component] --> [Example 1] +[Example 1] --> [Folder 3] +[Folder 3] --> [Frame 4] diff --git a/snippets/org-mode/deployment-diagram b/snippets/org-mode/deployment-diagram new file mode 100644 index 0000000..6350c03 --- /dev/null +++ b/snippets/org-mode/deployment-diagram @@ -0,0 +1,31 @@ +# -*- mode: snippet -*- +# contributor: Howard Abrams +# name: deployment-diagram +# key: deployment +# condition: (ha/org-nested-in-plantuml-block) +# group: plantuml +# -- +' See details at https://plantuml.com/deployment-diagram +agent agent +artifact artifact +boundary boundary +card card +circle circle +cloud cloud +collections collections +component component +control control +database database +entity entity +file file +folder folder +frame frame +interface interface +label label +node node +package package +queue queue +stack stack +rectangle rectangle +storage storage +usecase usecase \ No newline at end of file diff --git a/snippets/org-mode/dm-screen b/snippets/org-mode/dm-screen new file mode 100644 index 0000000..2181ce0 --- /dev/null +++ b/snippets/org-mode/dm-screen @@ -0,0 +1,12 @@ +# -*- mode: snippet -*- +# name: dm-screen +# key: __dm +# -- +#+TITLE: ${1:`(->> (buffer-file-name) + (file-name-base) + (s-split-words) + (--map (s-capitalize it)) + (s-join " "))`} +#+AUTHOR: Howard X. Abrams +#+EMAIL: howard.abrams@gmail.com +#+FILETAGS: :rpg:5e:dm-screen: diff --git a/snippets/org-mode/document-property-header b/snippets/org-mode/document-property-header new file mode 100644 index 0000000..511d13c --- /dev/null +++ b/snippets/org-mode/document-property-header @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: document-property-header +# key: topprop +# -- +#+PROPERTY: header-args${1::emacs-lisp} :${2:results} ${3:silent} \ No newline at end of file diff --git a/snippets/org-mode/emacs-lisp-code b/snippets/org-mode/emacs-lisp-code new file mode 100644 index 0000000..ee96def --- /dev/null +++ b/snippets/org-mode/emacs-lisp-code @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: emacs-lisp-code +# key: +# name: object-diagram +# key: object +# condition: (ha/org-nested-in-plantuml-block) +# group: plantuml +# -- +' See details at https://plantuml.com/object-diagram +object Extends +object Extension +object Composes +object Composition +object Aggregates +object Aggregation +object Relates +object Relation + +Extends <|-- Extension +Composes *-- Composition +Aggregates o-- "4" Aggregation +Relates .. Relation : some labels \ No newline at end of file diff --git a/snippets/org-mode/onboard b/snippets/org-mode/onboard new file mode 100644 index 0000000..14f14f8 --- /dev/null +++ b/snippets/org-mode/onboard @@ -0,0 +1,14 @@ +# -*- mode: snippet -*- +# name: onboard +# key: onboard +# -- +** TODO $1 + +Status: + - Waiting to fill out Requirements Document + +Summary: + - Initial meeting on `(format-time-string "<%Y-%m-%d %a>")` + +Personnel: + - $2 diff --git a/snippets/org-mode/plantuml b/snippets/org-mode/plantuml new file mode 100644 index 0000000..d3125fe --- /dev/null +++ b/snippets/org-mode/plantuml @@ -0,0 +1,14 @@ +# -*- mode: snippet -*- +# name: plantuml +# key:

+# group: plantuml +# -- + +#+begin_src plantuml :file ${1:`(ha/org-next-image-number)`}.${2:png} :exports file :results file +@startuml +!include plantuml-dark-theme.puml +' See details at https://plantuml.com/ +$0 +@enduml +#+end_src diff --git a/snippets/org-mode/rpgdm-npc b/snippets/org-mode/rpgdm-npc new file mode 100644 index 0000000..10dcc6d --- /dev/null +++ b/snippets/org-mode/rpgdm-npc @@ -0,0 +1,6 @@ +# -*- mode: snippet -*- +# name: RPG DM NPC +# key: rpgdm-npc +# -- + +Name: `name` \ No newline at end of file diff --git a/snippets/org-mode/section-property b/snippets/org-mode/section-property new file mode 100644 index 0000000..31270ea --- /dev/null +++ b/snippets/org-mode/section-property @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: section-property +# key: prop +# -- +:PROPERTIES: +:header-args:${1:emacs-lisp} :${2:results} ${3:silent} +:END: \ No newline at end of file diff --git a/snippets/org-mode/sequence-diagram b/snippets/org-mode/sequence-diagram new file mode 100644 index 0000000..1384c59 --- /dev/null +++ b/snippets/org-mode/sequence-diagram @@ -0,0 +1,13 @@ +# -*- mode: snippet -*- +# contributor: Howard Abrams +# name: sequence-diagram +# key: sequence +# condition: (ha/org-nested-in-plantuml-block) +# group: plantuml +# -- +' See details at https://plantuml.com/sequence-diagram +Alice -> Bob: Authentication Request +Bob --> Alice: Authentication Response + +Alice -> Bob: Another authentication Request +Alice <-- Bob: Another authentication Response \ No newline at end of file diff --git a/snippets/org-mode/shell-script-code b/snippets/org-mode/shell-script-code new file mode 100644 index 0000000..d724f19 --- /dev/null +++ b/snippets/org-mode/shell-script-code @@ -0,0 +1,7 @@ +# -*- mode: snippet -*- +# name: shell-script-code +# key: +# name: state-diagram +# key: state +# condition: (ha/org-nested-in-plantuml-block) +# group: plantuml +# -- +' See details at https://plantuml.com/state-diagram +[*] --> State1 +State1 --> [*] +State1 : this is a string +State1 : this is another string + +State1 -> State2 +State2 --> [*] \ No newline at end of file diff --git a/snippets/org-mode/timing-diagram b/snippets/org-mode/timing-diagram new file mode 100644 index 0000000..46865d0 --- /dev/null +++ b/snippets/org-mode/timing-diagram @@ -0,0 +1,21 @@ +# -*- mode: snippet -*- +# contributor: Howard Abrams +# name: timing-diagram +# key: timing +# condition: (ha/org-nested-in-plantuml-block) +# group: plantuml +# -- +' See details at https://plantuml.com/timing-diagram +robust "Web Browser" as WB +concise "Web User" as WU + +@0 +WU is Idle +WB is Idle + +@100 +WU is Waiting +WB is Processing + +@300 +WB is Waiting \ No newline at end of file diff --git a/snippets/org-mode/title b/snippets/org-mode/title new file mode 100644 index 0000000..35fe99a --- /dev/null +++ b/snippets/org-mode/title @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: title +# key: title +# -- +#+TITLE: ${0} \ No newline at end of file diff --git a/snippets/org-mode/use-case-diagram b/snippets/org-mode/use-case-diagram new file mode 100644 index 0000000..adec119 --- /dev/null +++ b/snippets/org-mode/use-case-diagram @@ -0,0 +1,12 @@ +# -*- mode: snippet -*- +# contributor: Howard Abrams +# name: use-case-diagram +# key: use-case +# condition: (ha/org-nested-in-plantuml-block) +# group: plantuml +# -- +' See details at https://plantuml.com/use-case-diagram +User -> (Start) +User --> (Use the application) : A small label + +:Main Admin: ---> (Use the application) : This is\nyet another\nlabel diff --git a/snippets/ruby-mode/rubocop-disable-comment b/snippets/ruby-mode/rubocop-disable-comment new file mode 100644 index 0000000..01b9627 --- /dev/null +++ b/snippets/ruby-mode/rubocop-disable-comment @@ -0,0 +1,5 @@ +# -*- mode: snippet -*- +# name: Rubocop disable comment +# key: rd +# -- +# rubocop:disable ${1:Lint/MethodLength} diff --git a/snippets/sh-mode/__ b/snippets/sh-mode/__ new file mode 100644 index 0000000..031f466 --- /dev/null +++ b/snippets/sh-mode/__ @@ -0,0 +1,12 @@ +# -*- mode: snippet -*- +# name: shell-init +# key: __ +# -- +#!/usr/bin/env `(if (equal (file-name-extension buffer-file-name) "zsh") "zsh" "bash")` +# ---------------------------------------------------------------------- +# `(upcase (file-name-nondirectory (file-name-sans-extension (buffer-file-name))))`: $0 +# ---------------------------------------------------------------------- + +set -euo pipefail + +$0 \ No newline at end of file diff --git a/snippets/sh-mode/getopt b/snippets/sh-mode/getopt new file mode 100644 index 0000000..ba0154e --- /dev/null +++ b/snippets/sh-mode/getopt @@ -0,0 +1,14 @@ +# -*- mode: snippet -*- +# name: getopt +# uuid: +# key: getopt +# -- +${1:OPT1}="${2:default value}" +$0 +while getopts "${3:s}" o +do case "$o" in + $3) $1="$OPTARG";; + [?]) usage;; + esac +done +shift $(expr $OPTIND - 1) diff --git a/snippets/sh-mode/usage b/snippets/sh-mode/usage new file mode 100644 index 0000000..af85bee --- /dev/null +++ b/snippets/sh-mode/usage @@ -0,0 +1,9 @@ +# -*- mode: snippet -*- +# name: usage +# key: usage +# -- + +function usage { + print >&2 "Usage: \$0 ${1:[-s] [-d seplist] file ...}" + $0exit 1 +} diff --git a/support/beep-notify.wav b/support/beep-notify.wav new file mode 100644 index 0000000..504b168 Binary files /dev/null and b/support/beep-notify.wav differ diff --git a/support/final-initialize.el b/support/final-initialize.el new file mode 100644 index 0000000..b916f29 --- /dev/null +++ b/support/final-initialize.el @@ -0,0 +1,75 @@ +;; -*- mode: emacs-lisp-mode -*- +;;; final-initialize.el --- Reinstall my Emacs configuration files -*- lexical-binding: t; -*- +;; +;; Copyright (C) 2020 Howard X. Abrams +;; +;; Author: Howard X. Abrams +;; Maintainer: Howard X. Abrams +;; Created: September 11, 2020 +;; Modified: September 11, 2020 +;; Version: 0.0.1 +;; Homepage: https://gitlab.com/howardabrams/hamacs +;; Package-Requires: ((emacs 27.1.50) (cl-lib "0.5")) +;; +;; This file is not part of GNU Emacs. +;; +;;; Commentary: +;; +;; Reinstall my Emacs configuration files +;; +;;; Code: + +(require 'ob) +(require 'f) + +;; The project source is actually defined in ~/.doom.d/hamacs-init.el +;; However, we define it here to get rid of Emacs linter warnings. ;-) +(defvar hamacs-source-dir + (f-parent (f-parent (or load-file-name (buffer-file-name)))) + "My configuration's source code directory.") + +;; Where should the code end up? +(defvar hamacs-private-code-dir (f-join user-emacs-directory "elisp") + "Location for most of the `hamacs' source code and configuration Lisp files.") + +(defun ha/install-by-tangling (source-files) + (dolist (file source-files) + (message "Tangling: %s" file) + (org-babel-tangle-file file))) + +(defun ha/install-code-from-essays () + "Tangle select website essays into the elisp bucket." + (ha/install-by-tangling + '("~/website/Technical/Emacs/getting-more-boxes-done.org" + "~/website/Technical/Emacs/getting-even-more-boxes-done.org" + "~/website/Technical/Emacs/beep-for-emacs.org" + "~/website/Technical/Emacs/focused-work.org"))) + +(defun ha/install-code-from-hamacs () + "Copy Emacs Lisp code without needing to translate." + (dolist (file (directory-files + (f-join hamacs-source-dir "elisp") t (rx bol (not ".")))) + (copy-file file (file-name-as-directory hamacs-private-code-dir) t))) + +(defun ha/install-configuration () + "Two primary jobs: tangle all source configuration files and link +non-tangled files." + (interactive) + (unless (f-exists? user-emacs-directory) + (make-directory user-emacs-directory)) + (unless (f-exists? hamacs-private-code-dir) + (make-directory hamacs-private-code-dir)) + + (ha/install-code-from-essays) + (ha/install-code-from-hamacs) + + ;; Link some source-controlled directories into ~/.doom.d ... + ;; This used to be a longer list. ;-) + (dolist (something '("snippets")) + (let ((dir (f-join user-emacs-directory something)) + (src (f-join default-directory something))) + (unless (f-symlink? dir) + (f-symlink src dir))))) + +(provide 'final-initialize) +;;; final-initialize.el ends here diff --git a/support/levitating-gnu.png b/support/levitating-gnu.png new file mode 100644 index 0000000..63d5ed2 Binary files /dev/null and b/support/levitating-gnu.png differ