├── .editorconfig ├── README.org ├── emacs.d ├── .mc-lists.el ├── abbrev_defs ├── dig-my-grave.el ├── early-init.el ├── ezf.el ├── gherkin-mode.el ├── git-related.el ├── grab-mac-link-revised.el ├── grab-x-link.el ├── hide-comnt.el ├── init.el ├── jf-campaign.el ├── jf-capf-hacking.el ├── jf-ef-themes.el ├── jf-gaming.el ├── jf-graveyard.org ├── jf-minor-mode-maker.el ├── jf-modus-themes.el ├── jf-pendragon.el ├── jf-project-theme-colors.el ├── jf-quick-help.el ├── jf-rubocop-cops.el ├── jf-the-one-ring.el ├── org-charsheet.el ├── ox-hugo-simple.el ├── personal.el ├── prot-common.el ├── prot-window.el ├── random-tables-data.el ├── random-tables-tor.el └── work.el ├── icons ├── bookish-gnu-alt.icns ├── bullish-gnu.png ├── gnu-standing-on-a-clown-sized-pile-of-books.icns ├── gnu-standing-on-piles-of-paper.icns ├── purple-gnu.icns ├── purple-gnu.jpg └── stained-glass-gnu.icns ├── lib ├── jf-horizontal-printing.setup ├── jf-journal.setup ├── jf-two-column.latex.setup ├── org-macros.setup └── treesit.rb ├── random-tables └── daytrippers │ └── mission-generator.txt ├── templates ├── treesit.rb └── venn.html /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.py] 13 | indent_style = space 14 | indent_size = 4 15 | 16 | [*.org] 17 | indent_size = 8 18 | indent_style = tab 19 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | #+title: README for Dotemacs 2 | #+AUTHOR: Jeremy Friesen 3 | #+EMAIL: jeremy@jeremyfriesen.com 4 | #+STARTUP: showall 5 | #+OPTIONS: toc:3 6 | 7 | This is my Emacs configuration repository. It pairs with my [[https://github.com/jeremyf/dotzshrc/][dotzshrc repository]]. The [[https://github.com/jeremyf/dotzshrc/blob/main/install.rb][install.rb]] script of the dotzshrc repository uses this repository. For implementation reasons, the =emacs.d= directory contains the init files that I link into my =$HOME/.emacs.d= directory. 8 | 9 | For those curious, I wrote [[https://takeonrules.com/2020/10/18/why-i-chose-emacs-as-my-new-text-editor/][Why I Chose Emacs as My New Text Editor // Take on Rules]]. 10 | 11 | * The Structure 12 | :PROPERTIES: 13 | :ID: 45A067AA-0193-4D6E-A77F-8E02B088DCBC 14 | :END: 15 | 16 | Below is the top-level directory: 17 | 18 | #+BEGIN_EXAMPLE 19 | > tree -L 1 20 | . 21 | ├── README.org 22 | ├── emacs.d 23 | ├── lib 24 | ├── random-tables 25 | └── templates 26 | 27 | 3 directories, 2 files 28 | #+END_EXAMPLE 29 | 30 | The files and directories: 31 | 32 | - README.org :: This file. 33 | - emacs.d :: A directory that contains files symlinked into =$HOME/.emacs.d= directory. 34 | - lib :: Some scripts I’ve written to help with my Emacs configuration. 35 | - random-tables :: Fodder for gaming, really not very well developed. 36 | - templates :: Templates for the [[https://github.com/minad/tempel][Emacs tempel package]]. 37 | 38 | * The emacs.d Directory 39 | :PROPERTIES: 40 | :ID: F12B8506-CA7F-4BC7-92DA-077C96AD2B80 41 | :END: 42 | 43 | There are two “primary” files to consider in =./emacs.d=: 44 | 45 | - [[file:emacs.d/init.el][init.el]] :: The file evaluated when Emacs launches. 46 | - early-init.el :: The file evaluated early on when Emacs launches. 47 | 48 | ** Digging Further into emacs.d 49 | :PROPERTIES: 50 | :ID: 9E740E8A-2098-41A5-B20F-72E1321AA97E 51 | :END: 52 | 53 | The =init.el= file requires the various files in my =emacs.d= directory. As of <2022-11-19 Sat> I shifted from Literate configuration to a Emacs Lisp configuration. I appreciate being able to jump to the definition and edit directly; something that wasn’t immediately obvious with Literate configuration. 54 | 55 | Organizing concepts is an interesting challenge, as packages interact in a multi-dimensional manner. Each of my =jf-.el= has a rudimentary commentary. 56 | 57 | - [[file:emacs.d/abbrev_defs][abbrev_defs]] :: Common typos auto-corrected 58 | - [[file:emacs.d/early-init.el][early-init.el]] :: The preamble for starting Emacs 59 | - [[file:emacs.d/hide-comnt.el][hide-comnt.el]] :: A silly little mode to hide comments 60 | - [[file:emacs.d/init.el][init.el]] :: Most everything of or related to my Emacs configuration. 61 | - [[file:emacs.d/dig-my-grave.el][dig-my-grave.el] :: An `org-mode` package that extends the triple back tick by providing a prompt for block context. 62 | - [[file:emacs.d/org-charsheet.el][org-charsheet.el]] :: A work-in-progress package for managing Org Headline metadata (with a focus on table top role-playing games). 63 | 64 | 65 | ** Keybinding Prefixes 66 | :PROPERTIES: 67 | :ID: EADA236A-4612-42D2-B5A7-7F73408C2AB8 68 | :END: 69 | 70 | On <2023-10-20 Fri> I began refactoring some keybindings. There were functions that got “prime” key chords but were not things I’d frequently use. As such, I’m taking a two-fold approach: 71 | 72 | 1. Create and document keybinding prefixes 73 | 2. Rely on the =which-key= package to provide bits of guidance. 74 | 75 | Those keybinding prefixes are as follows: 76 | 77 | 1. =C-c y= :: *Yank* something; as insert some text. 78 | 2. =C-c w= :: *Wrap* something; as in wrap the /current region/ by inserting text before and after the region. 79 | 3. =C-c f= :: *Find* something; search the file or project for something. 80 | 4. =C-c j= :: *Jump* to something; I have =C-j= bound =avy-goto-char-timer= as a quick in buffer jumper. And =C-c C-j= as =jf/project/jump-to-task=; which provides another context 81 | 82 | There are foundational keys that are deeply bound to muscle memory: 83 | 84 | - =C-s= :: For =consult-line=; a mini-buffer search and go to line within file. Akin to a feature I used in TextMate, Sublime, and Atom. 85 | - =s-t= :: For =consult-projectile=; a very potent navigation through buffers, files, and projects. 86 | -------------------------------------------------------------------------------- /emacs.d/.mc-lists.el: -------------------------------------------------------------------------------- 1 | ;; This file is automatically generated by the multiple-cursors extension. 2 | ;; It keeps track of your preferences for running commands with multiple cursors. 3 | 4 | (setq mc/cmds-to-run-for-all 5 | '( 6 | beginning-of-buffer 7 | comment-line 8 | consult-yank 9 | crux-move-beginning-of-line 10 | duplicate-line 11 | electric-pair-delete-pair 12 | end-of-buffer 13 | end-of-visual-line 14 | enh-ruby-electric-brace 15 | fill-paragraph 16 | git-rebase-squash 17 | hungry-delete-backward 18 | hungry-delete-forward 19 | indent-for-tab-command 20 | jf/delete-region-or-backward-word 21 | jf/kill-line-or-region 22 | jf/kill-region-or-backward-word 23 | kill-visual-line 24 | magit-status 25 | markdown-cycle 26 | markdown-outdent-or-delete 27 | mouse-set-region 28 | ns-copy-including-secondary 29 | ns-do-show-character-palette 30 | org-ctrl-c-minus 31 | org-cycle 32 | org-end-of-line 33 | org-force-self-insert 34 | org-insert-tilde 35 | org-metaleft 36 | org-metaright 37 | org-self-insert-command 38 | org-todo 39 | org-yank 40 | ruby-interpolation-insert 41 | sp-backward-delete-char 42 | string-inflection-all-cycle 43 | typopunct-insert-ellipsis-or-middot 44 | typopunct-insert-mp 45 | typopunct-insert-quotation-mark 46 | typopunct-insert-times 47 | typopunct-insert-typographical-dashes 48 | wdired-finish-edit 49 | wgrep-finish-edit 50 | whole-line-or-region-comment-dwim-2 51 | yaml-electric-backspace 52 | yaml-electric-dash-and-dot 53 | yas-expand 54 | )) 55 | 56 | (setq mc/cmds-to-run-once 57 | '( 58 | centaur-tabs-forward-tab 59 | consult-projectile 60 | ivy-occur-click 61 | ivy-switch-buffer 62 | mark-whole-buffer 63 | org-ctrl-c-ctrl-c 64 | org-export-dispatch 65 | org-insert-backtick 66 | rspec-toggle-spec-and-target 67 | rspec-verify-all 68 | swiper 69 | xref-find-apropos 70 | )) 71 | -------------------------------------------------------------------------------- /emacs.d/dig-my-grave.el: -------------------------------------------------------------------------------- 1 | ;;; dig-my-grave --- Expand triple backtick/grave character -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2023 Jeremy Friesen 4 | ;; Author: Jeremy Friesen 5 | 6 | ;; This file is NOT part of GNU Emacs. 7 | 8 | ;;; License 9 | 10 | ;; Copyright 2023 Jeremy Friesen 11 | ;; 12 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 13 | ;; you may not use this file except in compliance with the License. 14 | ;; You may obtain a copy of the License at 15 | ;; 16 | ;; http://www.apache.org/licenses/LICENSE-2.0 17 | ;; 18 | ;; Unless required by applicable law or agreed to in writing, software 19 | ;; distributed under the License is distributed on an "AS IS" BASIS, 20 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | ;; See the License for the specific language governing permissions and 22 | ;; limitations under the License. 23 | 24 | ;;; Commentary: 25 | 26 | ;; I used to write a lot of Markdown. And the triple grave character 27 | ;; (e.g. “```”) is a wonderful convenience for indicating code blocks. This 28 | ;; package builds on that idea and repurposes the triple backtick to a quick 29 | ;; prompt that feels idiomatic to Markdown's triple backtick. 30 | 31 | ;;; Code: 32 | 33 | (require 'org) 34 | 35 | (defvar dig-my-grave/templates-alist/org-mode 36 | '(("Bash" . "#+begin_src bash :results scalar replace :exports both :tangle yes\n#+end_src") 37 | ("Blockquote" . jf/org-mode/insert-block/quote_block) 38 | ("Details and Summary" . "#+begin_details\n#+begin_summary\n\n#+end_summary\n#+end_details") 39 | ("Emacs Lisp" . "#+begin_src emacs-lisp\n#+end_src") 40 | ("Gherkin" . "#+begin_src gherkin\n#+end_src") 41 | ("Go Lang" . "#+begin_src go-ts\n#+end_src") 42 | ("Org Structure" . org-insert-structure-template) 43 | ("Elixir" . "#+begin_src elixir-ts\n#+end_src") 44 | ("Plant UML (puml)" . "#+begin_src plantuml\n@startuml\n!theme amiga\n\n@enduml\n#+end_src") 45 | ("Ruby" . "#+begin_src ruby\n#+end_src") 46 | ("Sudo" . "#+begin_src shell :dir \"/sudo::/\" :cache no :export source :results raw silent\n#+end_src") 47 | ("Update" . tempel-insert-update_block) 48 | ("Verb" . tempel-insert-verb_block)) 49 | 50 | "A list of `cons' cells used for `dig-my-grave' `completing-read'. 51 | The `car' as the label and `cdr' as the value that we'll insert.") 52 | 53 | (defun jf/org-mode/insert-block/quote_block (author cite cite_url) 54 | (interactive (list (read-string "Author: ") 55 | (read-string "Cite: ") 56 | (read-string "Cite URL: "))) 57 | ;; TODO Extract the attr_shortcode functionality to be able to insert this. 58 | (insert (when (or (s-present? author) (s-present? cite) (s-present? cite_url)) "#+attr_shortcode:") 59 | (if (s-present? author) (concat " :pre " author) "") 60 | (if (s-present? cite) (concat " :cite " cite) "") 61 | (if (s-present? cite_url) (concat " :cite_url " cite_url) "") 62 | "\n#+begin_quote\n\n#+end_quote") 63 | (re-search-backward "^$")) 64 | 65 | (define-key org-mode-map (kbd "`") #'dig-my-grave) 66 | (defun dig-my-grave () 67 | "Prompt to `insert' block when 3 consecutive graves (e.g. “`”) start line. 68 | 69 | See `dig-my-grave/templates-alist/org-mode'." 70 | (interactive) 71 | (if (and (= (current-column) 2) (looking-back "``" (- (point) 2))) 72 | ;; We have just hit our third backtick at the beginning of the line. 73 | (progn 74 | (delete-char -2) 75 | ;; I use the alist-get pattern a lot...perhaps a function? 76 | (let ((value (alist-get (completing-read "Block Type: " 77 | dig-my-grave/templates-alist/org-mode nil t) 78 | dig-my-grave/templates-alist/org-mode nil nil #'string=))) 79 | (cond 80 | ;; Let's assume that we're dealing with registered org blocks. 81 | ((stringp value) 82 | (insert value) (forward-line -1) (org-edit-special)) 83 | ;; Trust the function 84 | ((commandp value) (call-interactively value)) 85 | ((functionp value) (funcall value)) 86 | ;; Time for a pull request 87 | (t (error "Unprocessable value %s for #'dig-my-grave" value))))) 88 | (setq last-command-event ?`) 89 | (call-interactively #'org-self-insert-command))) 90 | 91 | (provide 'dig-my-grave) 92 | ;;; dig-my-grave.el ends here 93 | -------------------------------------------------------------------------------- /emacs.d/early-init.el: -------------------------------------------------------------------------------- 1 | ;; A startup optimization with regard to startup speed optimisation. 2 | ;; Here I am storing the default value with the intent of restoring it 3 | ;; via the `emacs-startup-hook'. 4 | (defvar jf-emacs--file-name-handler-alist file-name-handler-alist) 5 | (defvar jf-emacs--vc-handled-backends vc-handled-backends) 6 | (setq file-name-handler-alist nil 7 | vc-handled-backends nil) 8 | 9 | ;; From https://jackjamison.xyz/blog/emacs-garbage-collection/ 10 | (defun jf/minibuffer-setup-hook () 11 | ;; With 12 | (setq gc-cons-threshold most-positive-fixnum)) 13 | (add-hook 'minibuffer-setup-hook 'jf/minibuffer-exit-hook) 14 | (defun jf/minibuffer-exit-hook () 15 | (setq gc-cons-threshold 80 * 1024 * 1024)) 16 | (add-hook 'minibuffer-exit-hook 'jf/minibuffer-exit-hook) 17 | (setopt gc-cons-threshold most-positive-fixnum) 18 | (run-with-idle-timer 1.2 t 'garbage-collect) 19 | 20 | (add-hook 'emacs-startup-hook 21 | (lambda () 22 | (setq 23 | file-name-handler-alist jf-emacs--file-name-handler-alist 24 | vc-handled-backends jf-emacs--vc-handled-backends))) 25 | 26 | ;; From straight.el, "Users of Emacs versions >= 27 will want to add 27 | ;; the following:" 28 | (setopt package-enable-at-startup nil) 29 | -------------------------------------------------------------------------------- /emacs.d/ezf.el: -------------------------------------------------------------------------------- 1 | ;;; ezf.el --- emacs fuzzy finder -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2022 Mickey Petersen 4 | 5 | ;; Author: Mickey Petersen 6 | ;; Keywords: tools, tools 7 | 8 | ;; This program is free software; you can redistribute it and/or modify 9 | ;; it under the terms of the GNU General Public License as published by 10 | ;; the Free Software Foundation, either version 3 of the License, or 11 | ;; (at your option) any later version. 12 | 13 | ;; This program is distributed in the hope that it will be useful, 14 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | ;; GNU General Public License for more details. 17 | 18 | ;; You should have received a copy of the GNU General Public License 19 | ;; along with this program. If not, see . 20 | 21 | ;;; Commentary: 22 | 23 | ;; Filter and select matches on the command line using your favourite 24 | ;; completion framework in Emacs. It's an Emacs version of `fzf', a 25 | ;; command line tool that does basic fuzzy finding and selection using 26 | ;; a curses-like program. 27 | ;; 28 | ;; Unlike `fzf' this package + a shell script uses your Emacs instead! 29 | ;; That means it works well in `shell-mode' -- or equally well in 30 | ;; internal or external terminals -- and with the benefit of calling 31 | ;; out to your existing Emacs instance using `emacsclient'. 32 | ;; 33 | ;; Pipe data into `ezf' and you'll be prompted to filter and select 34 | ;; from the candidates in Emacs. 35 | ;; 36 | 37 | ;;; Examples: 38 | 39 | ;; 40 | ;; # Search for debian packages and pass the matches to `ezf' 41 | ;; $ apt-cache search emacs | ezf -f 1 42 | ;; 43 | ;; # Filter matches from `find' and pass them to `wc' 44 | ;; $ wc -l $(find . -name '*.txt' | ezf) 45 | ;; 46 | 47 | ;;; Code: 48 | 49 | (defun ezf-default (filename) 50 | "Complete candidates in FILENAME with `completing-read'." 51 | (completing-read-multiple 52 | "Pick a Candidate: " 53 | (with-temp-buffer 54 | (insert-file-contents-literally filename nil) 55 | (string-lines (buffer-string) t)))) 56 | 57 | (defvar ezf-separators " " 58 | "Regexp of separators `ezf' should use to split a line.") 59 | 60 | (defun ezf-1 (candidates &optional field) 61 | (when field 62 | (setq field 63 | (if (and (stringp field) (string-match "-" field)) 64 | (split-string field "-" t) 65 | (string-to-number field)))) 66 | (mapconcat (lambda (candidate) 67 | (cond ((numberp field) 68 | ;; The field column of line. 69 | (identity 70 | (nth field 71 | (split-string candidate ezf-separators t " ")))) 72 | ((consp field) 73 | (let* ((beg (string-to-number (car field))) 74 | (end (cadr field)) 75 | (split (split-string 76 | candidate ezf-separators t " ")) 77 | (len (length split)) 78 | (lst (nthcdr beg split))) 79 | (if (and end 80 | (< (setq end (string-to-number end)) len)) 81 | ;; The line part from beg to end. 82 | (mapconcat 'identity 83 | (nbutlast lst (1- (- len end))) 84 | " ") 85 | ;; The line part from beg to eol. 86 | (mapconcat 'identity lst " ")))) 87 | ;; The whole line. 88 | (t candidate))) 89 | candidates 90 | " ")) 91 | 92 | (defun ezf (filename &optional field completing-fn) 93 | "Wrapper that invokes COMPLETION-FN with FILENAME. 94 | 95 | Optionally split each line of string by `ezf-separators' if FIELD 96 | is non-nil and return FIELD. FIELD can be specified as a range of 97 | columns like \"1-\" or \"1-6\", otherwise it is specified as a string 98 | representing an integer e.g. \"1\". 99 | 100 | If COMPLETING-FN is nil default to `ezf-default'." 101 | (when-let ((candidates (funcall (or completing-fn 'ezf-helm) filename))) 102 | (ezf-1 candidates field))) 103 | 104 | (provide 'ezf) 105 | ;;; ezf.el ends here 106 | -------------------------------------------------------------------------------- /emacs.d/gherkin-mode.el: -------------------------------------------------------------------------------- 1 | ;;; gherkin-mode --- Font locks for gherkin syntax -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2023 Jeremy Friesen 4 | ;; Author: Jeremy Friesen 5 | 6 | ;; This file is NOT part of GNU Emacs. 7 | 8 | ;;; License 9 | 10 | ;; Copyright 2023 Jeremy Friesen 11 | ;; 12 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 13 | ;; you may not use this file except in compliance with the License. 14 | ;; You may obtain a copy of the License at 15 | ;; 16 | ;; http://www.apache.org/licenses/LICENSE-2.0 17 | ;; 18 | ;; Unless required by applicable law or agreed to in writing, software 19 | ;; distributed under the License is distributed on an "AS IS" BASIS, 20 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 | ;; See the License for the specific language governing permissions and 22 | ;; limitations under the License. 23 | 24 | ;;; Commentary 25 | 26 | ;; A simple package for providing font-locks for Gherkin syntax. I find myself 27 | ;; writing Gherkin Mode inside Org-Mode code blocks. 28 | ;; 29 | ;; https://cucumber.io/docs/gherkin/ 30 | 31 | ;;; Code 32 | 33 | (define-derived-mode gherkin-mode prog-mode 34 | "Gherkin" 35 | "A mode for writing Gherkin syntax documentation." 36 | :group 'gherkin-mode 37 | (make-local-variable 'comment-start) 38 | (setq comment-start "# ")) 39 | 40 | (font-lock-add-keywords 'gherkin-mode 41 | '(("^[[:space:]]*\\(Given\\|When\\|Then\\|But\\|And\\)" . 'font-lock-keyword-face) 42 | ("^[[:space:]]*\\(Feature\\|Background\\|Scenario\\|Scenario Outline\\|Examples?\\|Scenarios\\):.*" . 'font-lock-doc-face) 43 | ("<[^>]*>" . 'font-lock-variable-name-face) 44 | ("^[[:space:]]*@.*" . 'font-lock-preprocessor-face) 45 | ("^[[:space:]]*#.*" . 'font-lock-comment-face))) 46 | 47 | (provide 'gherkin-mode) 48 | ;;; gherkin-mode.el ends here 49 | -------------------------------------------------------------------------------- /emacs.d/git-related.el: -------------------------------------------------------------------------------- 1 | ;;; git-related.el --- Find related files through commit history analysis -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2023 Nthcdr 4 | 5 | ;; Author: Nthcdr 6 | ;; Maintainer: Nthcdr 7 | ;; Contributions: Jeremy Friesen 8 | ;; URL: https://macroexpand.net/el/git-related.el 9 | ;; Version: 1.0 10 | ;; Package-Requires: ((emacs "28.1")) 11 | 12 | ;; This program is free software; you can redistribute it and/or modify 13 | ;; it under the terms of the GNU General Public License as published by 14 | ;; the Free Software Foundation, either version 3 of the License, or 15 | ;; (at your option) any later version. 16 | 17 | ;; This program is distributed in the hope that it will be useful, 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | ;; GNU General Public License for more details. 21 | 22 | ;; You should have received a copy of the GNU General Public License 23 | ;; along with this program. If not, see . 24 | 25 | ;;; Commentary: 26 | 27 | ;; Find files by recommendation based on git commit history. 28 | 29 | ;; Usage: Visiting a git versioned file run once (and then only when 30 | ;; you feel the need to refresh) `consult-git-related-update' than you will get 31 | ;; suggestions based on the current file through invocations to 32 | ;; `consult-git-related-find-file' 33 | 34 | ;;; Todo: 35 | 36 | ;; Test that the graph exists, if not, run the command for the graph then 37 | ;; proceed. 38 | 39 | ;;; Code: 40 | 41 | (require 'cl-lib) 42 | (require 'subr-x) 43 | (require 'project) 44 | (require 'vc-git) 45 | (require 'consult) 46 | 47 | (defvar git-related--graphs nil) 48 | 49 | (cl-defstruct git-related--graph files commits) 50 | (cl-defstruct git-related--file (name "" :type string) (commits nil :type list)) 51 | (cl-defstruct git-related--commit (sha "" :type string) (files nil :type list)) 52 | 53 | (defun git-related--new-graph () 54 | "Create an empty graph." 55 | (make-git-related--graph 56 | :files (make-hash-table :test 'equal :size 2500) 57 | :commits (make-hash-table :test 'equal :size 2500))) 58 | 59 | (defun git-related--record-commit (graph sha filenames) 60 | "Record in the GRAPH the relation between SHA and FILENAMES." 61 | (let ((commit (make-git-related--commit :sha sha))) 62 | (dolist (filename filenames) 63 | (let* ((seen-file (gethash filename (git-related--graph-files graph))) 64 | (file-found (not (null seen-file))) 65 | (file (or seen-file (make-git-related--file :name filename)))) 66 | 67 | (cl-pushnew commit (git-related--file-commits file)) 68 | (cl-pushnew file (git-related--commit-files commit)) 69 | 70 | (unless file-found 71 | (setf (gethash filename (git-related--graph-files graph)) file)))) 72 | 73 | (setf (gethash sha (git-related--graph-commits graph)) commit))) 74 | 75 | (defun git-related--replay (&optional graph) 76 | "Replay git commit history into optional GRAPH." 77 | (let ((graph (or graph (git-related--new-graph)))) 78 | (with-temp-buffer 79 | (process-file vc-git-program nil t nil "log" "--name-only" "--format=%x00%H") 80 | (let* ((commits (split-string (buffer-string) "\0" t)) 81 | (replay-count 0) 82 | (progress-reporter (make-progress-reporter "Building commit-file graph..." 0 (length commits)))) 83 | (dolist (commit commits) 84 | (let* ((sha-and-paths (split-string commit "\n\n" t (rx whitespace))) 85 | (sha (car sha-and-paths)) 86 | (paths (when (cadr sha-and-paths) 87 | (split-string (cadr sha-and-paths) "\n" t (rx whitespace))))) 88 | (git-related--record-commit graph sha paths) 89 | (progress-reporter-update progress-reporter (cl-incf replay-count)))) 90 | (progress-reporter-done progress-reporter))) 91 | graph)) 92 | 93 | (cl-defun git-related--similar-files (&key graph filename) 94 | "Return files in GRAPH that are similar to FILENAME." 95 | (unless (git-related--graph-p graph) 96 | (user-error "You need to index this project first")) 97 | (when-let ((file (gethash filename (git-related--graph-files graph)))) 98 | 99 | (let ((file-sqrt (sqrt (length (git-related--file-commits file)))) 100 | (neighbor-sqrts (make-hash-table :test 'equal :size 100)) 101 | (hits (make-hash-table :test 'equal :size 100))) 102 | 103 | (dolist (commit (git-related--file-commits file)) 104 | (dolist (neighbor (remove file (git-related--commit-files commit))) 105 | (let ((count (cl-incf (gethash (git-related--file-name neighbor) hits 0)))) 106 | (when (= count 1) 107 | (setf (gethash (git-related--file-name neighbor) neighbor-sqrts) 108 | (sqrt (length (git-related--file-commits neighbor)))))))) 109 | 110 | (let (ranked-neighbors) 111 | (maphash 112 | (lambda (neighbor-name neighbor-sqrt) 113 | (let ((axb (* file-sqrt neighbor-sqrt)) 114 | (n (gethash neighbor-name hits))) 115 | (push (list (if (cl-plusp axb) (/ n axb) 0.0) neighbor-name) ranked-neighbors))) 116 | neighbor-sqrts) 117 | ;; We want to sort in descending score order. Thus the more "related" 118 | ;; files are at the beginning of the list. 119 | (cl-sort 120 | (cl-remove-if-not #'git-related--file-exists-p ranked-neighbors :key #'cadr) 121 | #'> :key #'car))))) 122 | 123 | (defun git-related--file-exists-p (relative-filename) 124 | "Determine if RELATIVE-FILENAME currently exists." 125 | (file-exists-p 126 | (expand-file-name relative-filename 127 | (project-root (project-current))))) 128 | 129 | (defun consult-git-related--propertize-hit (hit) 130 | "Given the cons HIT return a rendered representation for completion." 131 | (propertize 132 | (cadr hit) 133 | 'score (car hit) 134 | 'path (cadr hit))) 135 | 136 | ;;;###autoload 137 | (defun consult-git-related-update () 138 | "Update graph for the current project." 139 | (interactive) 140 | (let* ((default-directory (project-root (project-current))) 141 | (project-symbol (intern (project-name (project-current)))) 142 | (graph (cl-getf git-related--graphs project-symbol))) 143 | (setf (cl-getf git-related--graphs project-symbol) 144 | (git-related--replay graph)))) 145 | 146 | ;;;###autoload 147 | (defun consult-git-related-find-file () 148 | "Find files related through commit history." 149 | (interactive) 150 | (if (buffer-file-name) 151 | (let ((default-directory (project-root (project-current)))) 152 | (find-file 153 | (when-let ((selection (consult-git-related--read))) 154 | (format "%s" selection)))) 155 | (user-error "Current buffer has no file"))) 156 | 157 | (defun consult-git-related--read () 158 | "A completing read function leveraging `consult-read'." 159 | (consult--read 160 | (consult--slow-operation "Building Git Relationships..." 161 | (mapcar #'consult-git-related--propertize-hit 162 | (git-related--similar-files 163 | :graph (cl-getf git-related--graphs (intern (project-name (project-current)))) 164 | :filename (file-relative-name (buffer-file-name) (project-root (project-current)))))) 165 | :prompt "Related files in Git history: " 166 | :category 'consult-git-related 167 | ;; This should be nil so we leverage the sort of the `git-related--similar-files' 168 | :sort nil 169 | :annotate #'consult-git-related--annotator 170 | :require-match t 171 | :history t)) 172 | 173 | (defun consult-git-related--annotator (cand) 174 | "Annotate the given CAND with it's score and modified date." 175 | (consult--annotate-align cand 176 | (format "%3.3f · %s" 177 | (get-text-property 0 'score cand) 178 | (format-time-string "%Y-%m-%d" (f-change-time cand))))) 179 | 180 | (provide 'git-related) 181 | 182 | ;;; git-related.el ends here 183 | -------------------------------------------------------------------------------- /emacs.d/grab-mac-link-revised.el: -------------------------------------------------------------------------------- 1 | (defun jf/default-browser (&optional name) 2 | "Set the default browser based on the given NAME." 3 | ;; brew install defaultbrowser 4 | (interactive 5 | (list 6 | (completing-read 7 | "Browser: " 8 | (mapcar 9 | (lambda (el) 10 | (s-trim el)) 11 | (s-split "\n" 12 | (s-trim 13 | (shell-command-to-string "defaultbrowser"))))))) 14 | (shell-command 15 | (format "defaultbrowser %s" (s-trim (s-replace "*" "" name))))) 16 | 17 | (use-package grab-mac-link 18 | ;; Grab a link from a variety of MacOS applications. 19 | :straight (:host github :repo "stevenbagley/grab-mac-link.el") 20 | ;; Ensuring we load these, as I'll need them later. 21 | :commands (grab-mac-link-safari-1 grab-mac-link-chrome-1 grab-mac-link-split) 22 | :config 23 | ;; A replacement function for existing grab-mac-link-make-html-link 24 | (defun jf/grab-mac-link-make-html-link (url name) 25 | "Using HTML syntax, link to and cite the URL with the NAME." 26 | (format (concat "" 27 | "" 28 | "%s" 29 | "" 30 | "") 31 | url name)) 32 | ;; The function advice to override the default behavior 33 | (advice-add 'grab-mac-link-make-html-link 34 | :override 'jf/grab-mac-link-make-html-link 35 | '((name . "jnf"))) 36 | :bind (("C-c g" . grab-mac-link-revised))) 37 | 38 | (defmacro grab-mac-link-register-firefox-fork-func (code macos_app_name) 39 | "Create a `grab-mac-link' function for MACOS_APP_NAME. 40 | 41 | By convention the CODE is used to generate the function we'll dispatch 42 | to fetch the link from the MACOS_APP_NAME. 43 | 44 | This macro should work for any Firefox-based application." 45 | (let ((fn 46 | (intern (format "grab-mac-link-%s-1" code)))) 47 | `(defun ,fn () 48 | (let ((result 49 | (do-applescript 50 | (concat 51 | "set oldClipboard to the clipboard\n" 52 | "set frontmostApplication to path to frontmost application\n" 53 | "tell application \"" ,macos_app_name "\"\n" 54 | " activate\n" 55 | " delay 0.15\n" 56 | " tell application \"System Events\"\n" 57 | " keystroke \"l\" using {command down}\n" 58 | " keystroke \"a\" using {command down}\n" 59 | " keystroke \"c\" using {command down}\n" 60 | " end tell\n" 61 | " delay 0.15\n" 62 | " set theUrl to the clipboard\n" 63 | " set the clipboard to oldClipboard\n" 64 | " set theResult to (get theUrl) & \"::split::\" & (get name of window 1)\n" 65 | "end tell\n" 66 | "activate application (frontmostApplication as text)\n" 67 | "set links to {}\n" 68 | "copy theResult to the end of links\n" 69 | "return links as string\n")))) 70 | (grab-mac-link-split 71 | (car (split-string result "[\r\n]+" t))))))) 72 | 73 | ;; TODO: Create data structure for registering an app. 74 | (defvar grab-mac-link-revised-apps 75 | '((?c . chrome) 76 | (?f . firefox) 77 | (?l . librewolf) 78 | (?m . mullvad) 79 | (?s . safari)) 80 | "A map of keys and application codes for `grab-mac-link-revised'. 81 | 82 | The `car' is the key, the `cdr' is the code.") 83 | 84 | (grab-mac-link-register-firefox-fork-func mullvad "Mullvad Browser") 85 | (grab-mac-link-register-firefox-fork-func librewolf "LibreWolf") 86 | (grab-mac-link-register-firefox-fork-func firefox "Firefox") 87 | 88 | ;;;###autoload 89 | (defun grab-mac-link-revised (app &optional link-type) 90 | "Prompt for an application to grab a link from. 91 | When done, go grab the link, and insert it at point. 92 | 93 | With a prefix argument, instead of \"insert\", save it to 94 | kill-ring. For org link, save it to `org-stored-links', then 95 | later you can insert it via `org-insert-link'. 96 | 97 | If called from lisp, grab link from APP and return it (as a 98 | string) with LINK-TYPE. APP is a symbol and must be one of 99 | '(chrome safari finder mail terminal), LINK-TYPE is also a symbol 100 | and must be one of '(plain markdown org), if LINK-TYPE is omitted 101 | or nil, plain link will be used." 102 | (interactive 103 | (let ((apps 104 | grab-mac-link-revised-apps) 105 | (link-types 106 | '((?p . plain) 107 | (?m . markdown) 108 | (?o . org) 109 | (?h . html))) 110 | (propertize-menu 111 | (lambda (string) 112 | "Propertize substring between [] in STRING." 113 | (with-temp-buffer 114 | (insert string) 115 | (goto-char 1) 116 | (while (re-search-forward "\\[\\(.+?\\)\\]" nil 'no-error) 117 | (replace-match (format "[%s]" (propertize (match-string 1) 'face 'bold)))) 118 | (buffer-string)))) 119 | input app link-type) 120 | (let ((message-log-max nil)) 121 | (message (funcall propertize-menu 122 | (format "Grab link from: %s" 123 | (mapconcat (lambda (data) 124 | (message "%S" (cdr data)) 125 | (replace-regexp-in-string 126 | (format "\\(%s\\).*$" (byte-to-string (car data))) 127 | (format "[%s]" (byte-to-string (car data))) 128 | (format "%s" (cdr data)) 129 | nil nil 1)) 130 | grab-mac-link-revised-apps 131 | " "))))) 132 | (setq input (read-char-exclusive)) 133 | (setq app (cdr (assq input grab-mac-link-revised-apps))) 134 | (let ((message-log-max nil)) 135 | (message (funcall propertize-menu 136 | (format "Grab link from %s as a [p]lain [m]arkdown [o]rg [h]tml link:" app)))) 137 | (setq input (read-char-exclusive)) 138 | (setq link-type (cdr (assq input link-types))) 139 | (list app link-type))) 140 | 141 | (setq link-type (or link-type 'plain)) 142 | (unless (and (memq app (mapcar (lambda (data) (cdr data)) grab-mac-link-revised-apps)) 143 | (memq link-type '(plain org markdown html))) 144 | (error "Unknown app %s or link-type %s" app link-type)) 145 | (let* ((grab-link-func (intern (format "grab-mac-link-%s-1" app))) 146 | (make-link-func (intern (format "grab-mac-link-make-%s-link" link-type))) 147 | (link (apply make-link-func (funcall grab-link-func)))) 148 | (when (called-interactively-p 'any) 149 | (if current-prefix-arg 150 | (if (eq link-type 'org) 151 | (let* ((res (funcall grab-link-func)) 152 | (link (car res)) 153 | (desc (cadr res))) 154 | (push (list link desc) org-stored-links) 155 | (message "Stored: %s" desc)) 156 | (kill-new link) 157 | (message "Copied: %s" link)) 158 | (insert link))) 159 | link)) 160 | -------------------------------------------------------------------------------- /emacs.d/grab-x-link.el: -------------------------------------------------------------------------------- 1 | ;;; grab-x-link.el --- Grab links from X11 apps and insert into Emacs -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright (C) 2016, 2018 Xu Chunyang 4 | ;; Copyright (C) 2025 Jeremy Friesen 5 | 6 | ;; Author: Xu Chunyang 7 | ;; Contributor: Jeremy Friesen 8 | ;; URL: https://github.com/jeremyf/grab-x-link 9 | ;; Package-Requires: ((emacs "28")) 10 | ;; Keywords: hyperlink 11 | ;; Version: 0.5 12 | 13 | ;; This program is free software; you can redistribute it and/or modify 14 | ;; it under the terms of the GNU General Public License as published by 15 | ;; the Free Software Foundation, either version 3 of the License, or 16 | ;; (at your option) any later version. 17 | 18 | ;; This program is distributed in the hope that it will be useful, 19 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ;; GNU General Public License for more details. 22 | 23 | ;; You should have received a copy of the GNU General Public License 24 | ;; along with this program. If not, see . 25 | 26 | ;;; Commentary: 27 | 28 | ;; Grab link and title from Firefox and Chromium, insert into Emacs buffer as 29 | ;; plain, markdown or org link. 30 | ;; 31 | ;; To use, invoke `M-x grab-x-link' and other commands provided by this package. 32 | ;; 33 | ;; Prerequisite: 34 | ;; - xdotool(1) 35 | ;; - xsel(1) or xclip(1) if you are running Emacs inside a terminal emulator 36 | ;; 37 | ;; Changes: 38 | ;; - 2025-04-28 v0.7 Refactor for Firefox-based browsers 39 | ;; - 2024-12-24 v0.6 Support Vivaldi 40 | ;; - 2018-02-05 v0.5 Support Google Chrome 41 | ;; - 2016-12-01 v0.4 Handle case that app is not running 42 | ;; - 2016-12-01 v0.3 Add the command `grab-x-link' 43 | ;; - 2016-11-19 v0.2 Rename grab-x11-link to grab-x-link 44 | ;; - 2016-11-19 v0.1 Support Emacs running inside terminal emulator 45 | 46 | ;;; Code: 47 | 48 | (declare-function org-make-link-string "org" (link &optional description)) 49 | 50 | (defun grab-x-link--shell-command-to-string (command) 51 | (with-temp-buffer 52 | (if (and (zerop (call-process-shell-command command nil t)) 53 | (> (buffer-size) 0)) 54 | (substring (buffer-string) 0 -1) 55 | nil))) 56 | 57 | (defun grab-x-link--build (url-title &optional type) 58 | "Build plain or markdown or org link." 59 | (let ((url (car url-title)) 60 | (title (cdr url-title))) 61 | (pcase type 62 | ('org (progn (require 'org) 63 | (org-make-link-string url title))) 64 | ('markdown (format "[%s](%s)" title url)) 65 | (_ (format "[%s](%s)" title url))))) 66 | 67 | (defun grab-x-link--title-strip (string suffix) 68 | "Remove SUFFIX from STRING." 69 | (cond 70 | ((< (length string) (length suffix)) 71 | string) 72 | ((string= (substring string (- (length string) (length suffix))) suffix) 73 | (substring string 0 (- (length suffix)))) 74 | (t string))) 75 | 76 | (defun grab-x-link--get-clipboard () 77 | "Get the contents of the clipboard. 78 | 79 | Favor native `gui-get-selection' but fallback to xsel and xclip." 80 | (cond 81 | ((and (display-graphic-p) (fboundp 'gui-get-selection)) 82 | (gui-get-selection)) 83 | ((executable-find "xsel") 84 | (grab-x-link--shell-command-to-string 85 | "xsel --clipboard")) 86 | ((executable-find "xclip") 87 | (grab-x-link--shell-command-to-string 88 | "xclip -selection clipboard -o")) 89 | (_ 90 | (error "Can't get clipboard, see `grab-x-link--get-clipboard' for details")))) 91 | 92 | (defvar grab-x-link-apps 93 | nil 94 | "A plist of registered apps for `grab-x-link'.") 95 | 96 | (defun grab-x-link (app &optional link-type apps) 97 | "Prompt for an application to grab a link from. 98 | When done, go gtab the link, and insert it at point. 99 | 100 | If called from Lisp, grab link APP and return it (as a string) in 101 | LINK-TYPE. APP is a symbol and must be one of '(chromium 102 | firefox), LINK-TYPE is also a symbol and must be one of '(plain 103 | markdown org), if LINK-TYPE is omitted or nil, plain link will be used." 104 | (interactive 105 | (let ((apps 106 | (mapcar (lambda (app) 107 | (cons 108 | (plist-get app :menu-key) 109 | (plist-get app :name))) 110 | grab-x-link-apps)) 111 | (link-types 112 | '((?p . plain) 113 | (?m . markdown) 114 | (?o . org))) 115 | (propertize-menu 116 | (lambda (string) 117 | "Propertize substring between [] in STRING." 118 | (with-temp-buffer 119 | (insert string) 120 | (goto-char 1) 121 | (while (re-search-forward "\\[\\(.+?\\)\\]" nil 'no-error) 122 | (replace-match (format "[%s]" (propertize (match-string 1) 'face 'bold)))) 123 | (buffer-string)))) 124 | input app link-type) 125 | 126 | (message (funcall propertize-menu 127 | (concat "Grab link from " 128 | (mapconcat (lambda (app) 129 | (plist-get app :menu-label)) 130 | grab-x-link-apps " ") 131 | ":"))) 132 | (setq input (read-char-exclusive)) 133 | (setq app (cdr (assq input apps))) 134 | 135 | (message (funcall propertize-menu 136 | (format "Grab link from %s as a [p]lain [m]arkdown [o]rg link:" app))) 137 | (setq input (read-char-exclusive)) 138 | (setq link-type (cdr (assq input link-types))) 139 | (list app link-type apps))) 140 | 141 | (unless link-type 142 | (setq link-type 'plain)) 143 | 144 | (unless (and (memq app (mapcar (lambda (cell) (cdr cell)) apps)) 145 | (memq link-type '(plain org markdown))) 146 | (error "Unknown app %s or link-type %s" app link-type)) 147 | 148 | (let ((link (grab-x-link--build 149 | (funcall (intern (format "grab-x-link-%s" app))) 150 | link-type))) 151 | (and (called-interactively-p 'any) (insert link)) 152 | link)) 153 | 154 | (cl-defmacro grab-x-link:register-app (&key menu-key menu-label classname name key suffix) 155 | "Leverage xdotool to copy the URL and title from the found window. 156 | 157 | We intersect the name and classname in xdotool. 158 | 159 | MENU-KEY: when choosing what to grab, this is the key to select the app. 160 | MENU-LABEL: label for the grab menu app name. 161 | NAME: the xdotool --name parameter 162 | CLASSNAME: the xdotool --classname parameter 163 | KEY: The key sequence to send to the application. 164 | SUFFIX: The suffix to strip from the returned title. 165 | " 166 | (let ((fn 167 | (intern (format "grab-x-link-%s" (downcase name))))) 168 | (add-to-list 'grab-x-link-apps 169 | (list :menu-key menu-key :menu-label menu-label :name (intern (downcase name)))) 170 | `(defun ,fn () 171 | (let ((emacs-window 172 | (grab-x-link--shell-command-to-string 173 | "xdotool getactivewindow")) 174 | (app-window 175 | (or 176 | (grab-x-link--shell-command-to-string 177 | (format "comm -12 <(xdotool search --name \"%s\" | sort) <(xdotool search --classname \"%s\" | sort)" ,name ,classname)) 178 | (error "Can't find %s Window -- is it running?" ,name)))) 179 | (shell-command 180 | (format "xdotool windowactivate --sync %s key %s" app-window ,key)) 181 | (shell-command 182 | (format "xdotool windowactivate %s" emacs-window)) 183 | (sit-for 0.2) 184 | (let ((url (substring-no-properties (grab-x-link--get-clipboard))) 185 | (title (grab-x-link--title-strip 186 | (grab-x-link--shell-command-to-string 187 | (concat "xdotool getwindowname " app-window)) 188 | ,suffix))) 189 | (cons url title)))))) 190 | 191 | (when (executable-find "firefox") 192 | (grab-x-link:register-app 193 | :menu-key ?f 194 | :menu-label "[f]irefox" 195 | :name "Firefox" 196 | :classname "Navigator" 197 | :key "ctrl+l Escape ctrl+l ctrl+c Escape" 198 | :suffix " - Mozilla Firefox")) 199 | 200 | (when (executable-find "librewolf") 201 | (grab-x-link:register-app 202 | :menu-key ?l 203 | :menu-label "[l]ibrewolf" 204 | :name "Librewolf" 205 | :classname "Navigator" 206 | :key "ctrl+l Escape ctrl+l ctrl+c Escape" 207 | :suffix " — LibreWolf")) 208 | 209 | (when (executable-find "mullvad-browser") 210 | (grab-x-link:register-app 211 | :menu-key ?m 212 | :menu-label "[m]ullvad" 213 | :name "Mullvad" 214 | :classname "Navigator" 215 | :key "ctrl+l Escape ctrl+l ctrl+c Escape" 216 | :suffix " - Mullvad Browser")) 217 | 218 | (bind-key "C-c g" 'grab-x-link) 219 | 220 | (provide 'grab-x-link) 221 | ;;; grab-x-link.el ends here 222 | -------------------------------------------------------------------------------- /emacs.d/hide-comnt.el: -------------------------------------------------------------------------------- 1 | ;;; hide-comnt.el --- Hide/show comments in code. 2 | ;; 3 | ;; Filename: hide-comnt.el 4 | ;; Description: Hide/show comments in code. 5 | ;; Author: Drew Adams 6 | ;; Maintainer: Drew Adams (concat "drew.adams" "@" "oracle" ".com") 7 | ;; Copyright (C) 2011-2019, Drew Adams, all rights reserved. 8 | ;; Created: Wed May 11 07:11:30 2011 (-0700) 9 | ;; Version: 0 10 | ;; Package-Requires: () 11 | ;; Last-Updated: Thu Nov 21 08:18:51 2019 (-0800) 12 | ;; By: dradams 13 | ;; Update #: 232 14 | ;; URL: https://www.emacswiki.org/emacs/download/hide-comnt.el 15 | ;; Doc URL: https://www.emacswiki.org/emacs/HideOrIgnoreComments 16 | ;; Keywords: comment, hide, show 17 | ;; Compatibility: GNU Emacs: 20.x, 21.x, 22.x, 23.x, 24.x, 25.x, 26.x 18 | ;; 19 | ;; Features that might be required by this library: 20 | ;; 21 | ;; None 22 | ;; 23 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 24 | ;; 25 | ;;; Commentary: 26 | ;; 27 | ;; Hide/show comments in code. 28 | ;; 29 | ;; Comments are hidden by giving them an `invisible' property with 30 | ;; value `hide-comment'. 31 | ;; 32 | ;; 33 | ;; Macros defined here: 34 | ;; 35 | ;; `with-comments-hidden'. 36 | ;; 37 | ;; Commands defined here: 38 | ;; 39 | ;; `hide/show-comments', `hide/show-comments-toggle'. 40 | ;; 41 | ;; User options defined here: 42 | ;; 43 | ;; `hide-whitespace-before-comment-flag', `ignore-comments-flag', 44 | ;; `show-invisible-comments-shows-all'. 45 | ;; 46 | ;; Non-interactive functions defined here: 47 | ;; 48 | ;; `hide/show-comments-1'. 49 | ;; 50 | ;; 51 | ;; Put this in your init file (`~/.emacs'): 52 | ;; 53 | ;; (require 'hide-comnt) 54 | ;; 55 | ;; 56 | ;; Note for Emacs 20: The commands and option defined here DO NOTHING 57 | ;; IN EMACS 20. Nevertheless, the library can be byte-compiled in 58 | ;; Emacs 20 and `hide-comnt.elc' can be loaded in later Emacs 59 | ;; versions and used there. This is the only real use of this 60 | ;; library for Emacs 20: it provides macro `with-comments-hidden'. 61 | ;; 62 | ;; Note for Emacs 21: It lacks the `comment-forward' function, so we 63 | ;; rely on the `comment-end' variable to determine the end of a 64 | ;; comment. This means that only one type of comment terminator is 65 | ;; supported. For example, `c++-mode' sets `comment-end' to "", 66 | ;; which is the convention for single-line comments ("// COMMENT"). 67 | ;; So "/* */" comments are treated as single-line commentsonly the 68 | ;; first line of such comments is hidden. The "*/" terminator is not 69 | ;; taken into account. 70 | ;; 71 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 72 | ;; 73 | ;;; Change Log: 74 | ;; 75 | ;; 2017/01/16 dadams 76 | ;; hide/show-comments-1: ((add-to|remove-from)-invisibility-spec 'hide-comment). 77 | ;; See https://github.com/syl20bnr/spacemacs/issues/8123. 78 | ;; 2016/12/27 dadams 79 | ;; Added: show-invisible-comments-shows-all. 80 | ;; hide/show-comments(-1): Respect show-invisible-comments-shows-all. 81 | ;; NOTE: Default behavior has changed: other invisible text is no longer made visible. 82 | ;; 2015/08/01 dadams 83 | ;; Added hide/show-comments-1. (And removed save-excursion around looking-back etc.) 84 | ;; hide/show-comments: 85 | ;; Use with-silent-modifications if available. Use hide/show-comments-1. 86 | ;; 2015/07/31 dadams 87 | ;; hide/show-comments: 88 | ;; Bind buffer-file-name to nil to inhibit ask-user-about-supersession-threat. 89 | ;; 2015/07/29 dadams 90 | ;; hide/show-comments: 91 | ;; No-op if no comment-start. Pass NOERROR arg to comment-normalize-vars. 92 | ;; 2014/11/05 dadams 93 | ;; hide/show-comments: 94 | ;; Use comment-forward even for "", so handle setting CEND correctly, e.g., for C++, 95 | ;; where comment-end is "" but multi-line comments are also OK. 96 | ;; Do not hide newline after single-line comments. 97 | ;; hide-whitespace-before-comment-flag non-nil no longer hides empty lines. 98 | ;; Prevent infloop for comment at bol. 99 | ;; Thx to Hinrik Sigurosson. 100 | ;; 2014/02/06 dadams 101 | ;; Added: hide-whitespace-before-comment-flag. 102 | ;; hide/show-comments: 103 | ;; Go to start of comment before calling comment-forward. 104 | ;; Hide whitespace preceding comment, if hide-whitespace-before-comment-flag. 105 | ;; 2013/12/26 dadams 106 | ;; hide/show-comments: Update START to comment end or END. 107 | ;; 2013/10/09 dadams 108 | ;; hide/show-comments: Use save-excursion. If empty comment-end go to CBEG. 109 | ;; Use comment-forward if available. 110 | ;; 2012/10/06 dadams 111 | ;; hide/show-comments: Call comment-normalize-vars first. Thx to Stefan Monnier. 112 | ;; hide/show-comments-toggle: Do nothing if newcomment.el not available. 113 | ;; 2012/05/10 dadams 114 | ;; Added: hide/show-comments-toggle. Thx to Denny Zhang for the suggestion. 115 | ;; 2011/11/23 dadams 116 | ;; hide/show-comments: Bug fix - ensure CEND is not past eob. 117 | ;; 2011/05/11 dadams 118 | ;; Created: moved code here from thing-cmds.el. 119 | ;; 120 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 121 | ;; 122 | ;; This program is free software; you can redistribute it and/or 123 | ;; modify it under the terms of the GNU General Public License as 124 | ;; published by the Free Software Foundation; either version 3, or 125 | ;; (at your option) any later version. 126 | ;; 127 | ;; This program is distributed in the hope that it will be useful, 128 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 129 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 130 | ;; General Public License for more details. 131 | ;; 132 | ;; You should have received a copy of the GNU General Public License 133 | ;; along with this program; see the file COPYING. If not, write to 134 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth 135 | ;; Floor, Boston, MA 02110-1301, USA. 136 | ;; 137 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 138 | ;; 139 | ;;; Code: 140 | 141 | 142 | (defvar comment-start) ; In `newcomment.el'. 143 | 144 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 145 | 146 | ;;;###autoload 147 | (defcustom ignore-comments-flag t 148 | "*Non-nil means macro `with-comments-hidden' hides comments." 149 | :type 'boolean :group 'matching) 150 | 151 | ;;;###autoload 152 | (defcustom hide-whitespace-before-comment-flag t 153 | "*Non-nil means `hide/show-comments' hides whitespace preceding a comment. 154 | It does not hide empty lines (newline chars), however." 155 | :type 'boolean :group 'matching) 156 | 157 | ;;;###autoload 158 | (defcustom show-invisible-comments-shows-all nil 159 | "Non-nil means `(hide/show-comments 'show ...)' shows all invisible text. 160 | The default value, nil, means it shows only text that was made 161 | invisible by `(hide/show-comments 'hide ...)'." 162 | :type 'boolean :group 'matching) 163 | 164 | 165 | (defmacro with-comments-hidden (start end &rest body) 166 | "Evaluate the forms in BODY while comments are hidden from START to END. 167 | But if `ignore-comments-flag' is nil, just evaluate BODY, 168 | without hiding comments. Show comments again when BODY is finished. 169 | 170 | See `hide/show-comments', which is used to hide and show the comments. 171 | Note that prior to Emacs 21, this never hides comments." 172 | (let ((result (make-symbol "result")) 173 | (ostart (make-symbol "ostart")) 174 | (oend (make-symbol "oend"))) 175 | `(let ((,ostart ,start) 176 | (,oend ,end) 177 | ,result) 178 | (unwind-protect (setq ,result (progn (when ignore-comments-flag 179 | (hide/show-comments 'hide ,ostart ,oend)) 180 | ,@body)) 181 | (when ignore-comments-flag (hide/show-comments 'show ,ostart ,oend)) 182 | ,result)))) 183 | 184 | ;;;###autoload 185 | (defun hide/show-comments (&optional hide/show start end) 186 | "Hide or show comments from START to END. 187 | Interactively, hide comments, or show them if you use a prefix arg. 188 | \(This is thus *NOT* a toggle command.) 189 | 190 | If option `hide-whitespace-before-comment-flag' is non-nil, then hide 191 | also any whitespace preceding a comment. 192 | 193 | Interactively, START and END default to the region limits, if active. 194 | Otherwise, including non-interactively, they default to `point-min' 195 | and `point-max'. 196 | 197 | Uses `save-excursion', restoring point. 198 | 199 | Option `show-invisible-comments-shows-all': 200 | 201 | * If non-nil then using this command to show invisible text shows 202 | *ALL* such text, regardless of how it was hidden. IOW, it does not 203 | just show invisible text that you previously hid using this command. 204 | 205 | * If nil (the default value) then using this command to show invisible 206 | text makes visible only such text that was previously hidden by this 207 | command. (More precisely, it makes visible only text whose 208 | `invisible' property has value `hide-comment'.) 209 | 210 | From Lisp, a HIDE/SHOW value of `hide' hides comments. Other values 211 | show them. 212 | 213 | This command does nothing in Emacs versions prior to Emacs 21, because 214 | it needs `comment-search-forward'." 215 | 216 | (interactive 217 | (cons (if current-prefix-arg 'show 'hide) 218 | (if (or (not mark-active) (null (mark)) (= (point) (mark))) 219 | (list (point-min) (point-max)) 220 | (if (< (point) (mark)) (list (point) (mark)) (list (mark) (point)))))) 221 | (when (and comment-start ; No-op if no comment syntax defined. 222 | (require 'newcomment nil t)) ; `comment-search-forward', Emacs 21+. 223 | (comment-normalize-vars 'NO-ERROR) ; Must call this first. 224 | (unless start (setq start (point-min))) 225 | (unless end (setq end (point-max))) 226 | (unless (<= start end) (setq start (prog1 end (setq end start)))) ; Swap. 227 | (if (fboundp 'with-silent-modifications) 228 | (with-silent-modifications ; Emacs 23+. 229 | (restore-buffer-modified-p nil) (hide/show-comments-1 hide/show start end)) 230 | (let ((bufmodp (buffer-modified-p)) ; Emacs < 23. 231 | (buffer-read-only nil) 232 | (buffer-file-name nil)) ; Inhibit `ask-user-about-supersession-threat'. 233 | (set-buffer-modified-p nil) 234 | (unwind-protect (hide/show-comments-1 hide/show start end) 235 | (set-buffer-modified-p bufmodp)))))) 236 | 237 | ;; Used only so that we can use `hide/show-comments' with older Emacs releases that do not 238 | ;; have macro `with-silent-modifications' and built-in `restore-buffer-modified-p', which 239 | ;; it uses. 240 | (defun hide/show-comments-1 (hide/show start end) 241 | "Helper for `hide/show-comments'." 242 | (let (cbeg cend) 243 | (if (eq 'hide hide/show) 244 | (add-to-invisibility-spec 'hide-comment) 245 | (remove-from-invisibility-spec 'hide-comment)) 246 | (save-excursion 247 | (goto-char start) 248 | (while (and (< start end) (save-excursion 249 | (setq cbeg (comment-search-forward end 'NOERROR)))) 250 | (goto-char cbeg) 251 | (save-excursion 252 | (setq cend (cond ((fboundp 'comment-forward) ; Emacs 22+ 253 | (if (comment-forward 1) 254 | (if (= (char-before) ?\n) (1- (point)) (point)) 255 | end)) 256 | ((string= "" comment-end) (min (line-end-position) end)) 257 | (t (search-forward comment-end end 'NOERROR))))) 258 | (when hide-whitespace-before-comment-flag ; Hide preceding whitespace. 259 | (if (fboundp 'looking-back) ; Emacs 22+ 260 | (when (looking-back "\n?\\s-*" nil 'GREEDY) 261 | (setq cbeg (match-beginning 0))) 262 | (while (memq (char-before cbeg) '(?\ ?\t ?\f)) (setq cbeg (1- cbeg))) 263 | (when (eq (char-before cbeg) ?\n) (setq cbeg (1- cbeg)))) 264 | ;; First line: Hide newline after comment. 265 | (when (and (= cbeg (point-min)) (= (char-after cend) ?\n)) 266 | (setq cend (min (1+ cend) end)))) 267 | (when (and cbeg cend) 268 | (if show-invisible-comments-shows-all 269 | (put-text-property cbeg cend 'invisible (and (eq 'hide hide/show) 270 | 'hide-comment)) 271 | (while (< cbeg cend) 272 | ;; Do nothing to text that is already invisible for some other reason. 273 | (unless (and (get-text-property cbeg 'invisible) 274 | (not (eq 'hide-comment (get-text-property cbeg 'invisible)))) 275 | (put-text-property cbeg (1+ cbeg) 'invisible (and (eq 'hide hide/show) 276 | 'hide-comment))) 277 | (setq cbeg (1+ cbeg))))) 278 | (goto-char (setq start (or cend end))))))) 279 | 280 | (defun hide/show-comments-toggle (&optional start end) 281 | "Toggle hiding/showing of comments in the active region or whole buffer. 282 | If the region is active then toggle in the region. Otherwise, in the 283 | whole buffer. 284 | 285 | This command does nothing in Emacs versions prior to Emacs 21, because 286 | it needs `comment-search-forward'. 287 | 288 | Interactively, START and END default to the region limits, if active. 289 | Otherwise, including non-interactively, they default to `point-min' 290 | and `point-max'. 291 | 292 | See `hide/show-comments' for more information." 293 | (interactive (if (or (not mark-active) (null (mark)) (= (point) (mark))) 294 | (list (point-min) (point-max)) 295 | (if (< (point) (mark)) (list (point) (mark)) (list (mark) (point))))) 296 | (when (require 'newcomment nil t) ; `comment-search-forward', Emacs 21+. 297 | (comment-normalize-vars) ; Must call this first. 298 | (if (save-excursion 299 | (goto-char start) 300 | (and (comment-search-forward end 'NOERROR) 301 | (if show-invisible-comments-shows-all 302 | (get-text-property (point) 'invisible) 303 | (eq 'hide-comment (get-text-property (point) 'invisible))))) 304 | (hide/show-comments 'show start end) 305 | (hide/show-comments 'hide start end)))) 306 | 307 | ;;;;;;;;;;;;;;;;;;;;;;;; 308 | 309 | (provide 'hide-comnt) 310 | 311 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 312 | ;;; hide-comnt.el ends here 313 | -------------------------------------------------------------------------------- /emacs.d/jf-campaign.el: -------------------------------------------------------------------------------- 1 | (defvar jf/campaign/file-name 2 | "~/git/org/denote/indices/20231127T184806--the-shadows-of-mont-brun-status-document__campaigns_projects_rpgs_StatusDocuments.org" 3 | "The current campaign file.") 4 | 5 | ;; Function assumes that we have a heading with a tag and direct subheading. 6 | (cl-defun jf/campaign/named-element (&key tag) 7 | "Fetch PROPERTY from headlines with parent that has given TAG." 8 | (with-current-buffer (find-file-noselect jf/campaign/file-name) 9 | (org-element-map 10 | (org-element-parse-buffer 'headline) 11 | 'headline 12 | (lambda (headline) 13 | (and (member tag 14 | (org-element-property 15 | :tags 16 | ;; Get immediate parent 17 | (car (org-element-lineage headline)))) 18 | (org-element-property :title headline)))))) 19 | 20 | (defun jf/campaign/random-npc-as-entry () 21 | "Create an NPC entry." 22 | (let* ((random-table/reporter 23 | ;; Bind a function that will only output the results of the 24 | ;; table, excluding the expression that generated the 25 | ;; results. 26 | (lambda (expression results) (format "%s" results))) 27 | (name 28 | (random-table/roll "In the Shadows of Mont Brun > Names")) 29 | (quirk 30 | (random-table/roll "Random NPC Quirks")) 31 | (alignment 32 | (random-table/roll "Errant > Alignment")) 33 | (lore-table 34 | (random-table/roll "The One Ring > Lore")) 35 | (locations 36 | (s-join ", " 37 | (completing-read-multiple 38 | "Location(s): " 39 | (jf/campaign/named-element :tag "locations")))) 40 | (factions 41 | (s-join ", " 42 | (completing-read-multiple 43 | "Faction(s): " 44 | (jf/campaign/named-element :tag "factions"))))) 45 | (format (concat 46 | "%s\n:PROPERTIES:\n::BACKGROUND:\n" 47 | ":LOCATIONS: %s\n:DEMEANOR:\n:ALIGNMENT: %s\n" 48 | ":QUIRKS: %s\n:FACTIONS: %s\n:END:\n\n%s") 49 | name locations alignment quirk factions lore-table))) 50 | 51 | (defun jf/campaign/populate-property-draw-value (&optional headline property table) 52 | "Populate HEADLINE's direct children's empty PROPERTY with roll on given TABLE. 53 | 54 | When given no HEADLINE, prompt for ones from the document. 55 | 56 | When given no PROPERTY, prompt for a property. 57 | 58 | When given no TABLE, prompt for an expression to evaluate via 59 | `random-table/roll'." 60 | (interactive) 61 | (let* ((level 1) 62 | (headline (or headline 63 | (completing-read "Headline: " 64 | (jf/org-get-headlines level) nil t))) 65 | (property (or property 66 | (org-read-property-name))) 67 | (table (or table 68 | (completing-read "Table: " 69 | random-table/storage/tables))) 70 | (random-table/reporter 71 | ;; Bind a function that will only output the results of the 72 | ;; table, excluding the expression that generated the 73 | ;; results. 74 | (lambda (expression results) (format "%s" results)))) 75 | (unless (org--valid-property-p property) 76 | (user-error "Invalid property name: \"%s\"" property)) 77 | (save-excursion 78 | ;; Now that we have the information, let’s start rolling on some 79 | ;; tables. 80 | (dolist 81 | ;; Because we will not be recalculating heading positions, we 82 | ;; need to reverse the list, as updates to the last element will 83 | ;; not alter the position of the next to last element. 84 | (subheading (reverse 85 | (org-element-map 86 | (org-element-parse-buffer) 87 | 'headline 88 | (lambda (el) 89 | (and 90 | ;; We want the children of the given level 91 | (= 92 | (+ 1 level) 93 | (org-element-property :level el)) 94 | (string= 95 | ;; Match the text 96 | (org-element-property 97 | :raw-value 98 | (car (org-element-lineage el))) 99 | headline) 100 | ;; Don't clobber already set values 101 | (not (org-element-property property el)) 102 | ;; We found a match now return it 103 | el))))) 104 | ;; We have finally found the element, now roll on the table and 105 | ;; populate. Unfortunately this does not re-calculate 106 | ;; positions. 107 | (org-entry-put 108 | (goto-char (org-element-property :begin subheading)) 109 | property (random-table/roll table)))))) 110 | -------------------------------------------------------------------------------- /emacs.d/jf-capf-hacking.el: -------------------------------------------------------------------------------- 1 | ;;; jf-capf-hacking --- -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2023 Jeremy Friesen 4 | ;; Author: Jeremy Friesen 5 | 6 | ;; This file is NOT part of GNU Emacs. 7 | 8 | ;;; Commentary: 9 | 10 | ;; Allow for completion of projects and then issues. Likely something I want to 11 | ;; include in commit messages. This behaves in a two step fashion: 12 | 13 | ;; - Type in the project (/project) 14 | ;; - Tab and select the project 15 | ;; - It fills in /project# 16 | ;; - Then type a number (e.g. /project#123) 17 | ;; - Tab and it will unfurl to a github issue URL 18 | 19 | ;;; Code: 20 | 21 | ;;;; Dependencies 22 | 23 | (require 'project) 24 | (require 'projectile) 25 | 26 | ;;;; Primary Functions: 27 | 28 | (defun jf/version-control/project-capf () 29 | "Complete project links." 30 | ;; While I'm going to replace "/project" I want to make sure that I don't 31 | ;; have any odd hits (for example /path/to/file) 32 | (when (looking-back "[^[:word:]]/[[:word:][:digit:]_\-]+" 33 | (jf/capf-max-bounds)) 34 | (let ((right (point)) 35 | (left (save-excursion 36 | (search-backward-regexp "/[[:word:][:digit:]_\-]+" 37 | (jf/capf-max-bounds) t) 38 | (point)))) 39 | (list left right 40 | (jf/version-control/known-project-names) 41 | :exit-function 42 | (lambda (text _status) 43 | (delete-char (- (length text))) 44 | (insert text "#")) 45 | :exclusive 'no)))) 46 | 47 | (defun jf/version-control/issue-capf () 48 | "Complete project issue links." 49 | ;; While I'm going to replace "/project" I want to make sure that I don't 50 | ;; have any odd hits (for example /path/to/file) 51 | (when (looking-back "[^[:word:]]/[[:word:][:digit:]_\-]+#[[:digit:]]+" 52 | (jf/capf-max-bounds)) 53 | (let ((right (point)) 54 | (left (save-excursion 55 | (search-backward-regexp 56 | "/[[:word:][:digit:]_\-]+#[[:digit:]]+" 57 | (jf/capf-max-bounds) t) 58 | (point)))) 59 | (list left right 60 | (jf/version-control/text) 61 | :exit-function 62 | #'jf/version-control/unfurl-issue-to-url 63 | :exclusive 'no)))) 64 | 65 | ;; (add-to-list 'completion-at-point-functions #'jf/version-control/issue-capf) 66 | ;; (add-to-list 'completion-at-point-functions #'jf/version-control/project-capf) 67 | 68 | ;;;; Service functions 69 | 70 | (cl-defun jf/capf-max-bounds (&key (window-size 40)) 71 | "Return the max bounds for `point' based on given WINDOW-SIZE." 72 | (let ((boundary (- (point) window-size))) 73 | (if (> 0 boundary) (point-min) boundary))) 74 | 75 | (cl-defun jf/version-control/known-project-names (&key (prefix "/")) 76 | "Return a list of project, prepending PREFIX to each." 77 | (mapcar (lambda (proj) 78 | (concat prefix (f-base proj))) 79 | projectile-known-projects)) 80 | 81 | (cl-defun jf/version-control/unfurl-project-as-issue-url-template (project &key (prefix "/")) 82 | "Return the issue URL template for the given PROJECT. 83 | 84 | Use the provided PREFIX to help compare against `projectile-known-projects'." 85 | (let* ((project-path 86 | (car (seq-filter (lambda (el) 87 | (or 88 | (s-ends-with? (concat project prefix) el) 89 | (s-ends-with? project el))) 90 | projectile-known-projects))) 91 | (remote 92 | (s-trim (shell-command-to-string 93 | (format "cd %s && git remote get-url origin" project-path))))) 94 | (if (s-ends-with? ".git" remote) 95 | (s-replace ".git" "/issues/%s" remote) 96 | (concat remote "/issues/%s")))) 97 | 98 | (defun jf/version-control/text () 99 | "Find all matches for project and issue." 100 | (s-match-strings-all "/[[:word:][:digit:]_\-]+#[[:digit:]]+" (buffer-string))) 101 | 102 | (defun jf/version-control/unfurl-issue-to-url (text _status) 103 | "Unfurl the given TEXT to a URL. 104 | 105 | Ignoring _STATUS." 106 | (delete-char (- (length text))) 107 | (let* ((parts (s-split "#" text)) 108 | (issue (cadr parts)) 109 | (project (or (car parts) (cdr (project-current))))) 110 | (insert (format 111 | (jf/version-control/unfurl-project-as-issue-url-template project) 112 | issue)))) 113 | 114 | (provide 'jf-capf-hacking) 115 | ;;; jf-capf-hacking.el ends here 116 | -------------------------------------------------------------------------------- /emacs.d/jf-ef-themes.el: -------------------------------------------------------------------------------- 1 | (use-package ef-themes 2 | :straight t 3 | :init 4 | (defvar jf/themes-plist '() 5 | "The named themes by pallette.") 6 | :config 7 | (setq ef-themes-headings ; read the manual's entry or the doc string 8 | '((0 . (bold 1.4)) 9 | (1 . (variable-pitch bold 1.7)) 10 | (2 . (overline semibold 1.5)) 11 | (3 . (monochrome overline 1.4 background)) 12 | (4 . (overline 1.3)) 13 | (5 . (rainbow 1.2)) 14 | (6 . (rainbow 1.15)) 15 | (t . (rainbow 1.1)))) 16 | ;; When these are non-nil, the mode line uses the proportional font 17 | (setq ef-themes-mixed-fonts t 18 | ef-themes-variable-pitch-ui t) 19 | 20 | (defun jf/theme-custom-faces () 21 | "Set the various custom faces for both `treesit' and `tree-sitter'." 22 | (ef-themes-with-colors 23 | (setq hl-todo-keyword-faces 24 | `(("HOLD" . ,yellow) 25 | ("TODO" . ,red) 26 | ("BLOCKED" . ,yellow) 27 | ("NEXT" . ,blue) 28 | ("THEM" . ,magenta) 29 | ("PROG" . ,cyan-warmer) 30 | ("OKAY" . ,green-warmer) 31 | ("DONT" . ,yellow-warmer) 32 | ("FAIL" . ,red-warmer) 33 | ("BUG" . ,red-warmer) 34 | ("DONE" . ,green) 35 | ("NOTE" . ,blue-warmer) 36 | ("KLUDGE" . ,cyan) 37 | ("HACK" . ,cyan) 38 | ("TEMP" . ,red) 39 | ("FIXME" . ,red-warmer) 40 | ("XXX+" . ,red-warmer) 41 | ("REVIEW" . ,red) 42 | ("DEPRECATED" . ,yellow))) 43 | (custom-set-faces 44 | `(denote-faces-link 45 | ((,c (:inherit link 46 | :box (:line-width (1 . 1) 47 | :color ,border 48 | :style released-button))))) 49 | `(ef-themes-fixed-pitch 50 | ((,c (:family "IntoneMono Nerd Font Mono")))) 51 | `(olivetti-fringe 52 | ((,c (:inherit fringe :background ,bg-dim)))) 53 | `(jf/bom-face 54 | ((,c (:width ultra-expanded 55 | :box (:line-width (2 . 2) 56 | :color ,underline-err 57 | :style released-button))))) 58 | `(jf/mode-line-format/face-shadow 59 | ((,c :foreground ,fg-mode-line))) 60 | `(jf/tabs-face 61 | ((,c :underline (:style wave :color ,bg-blue-intense)))) 62 | `(jf/org-faces-date 63 | ((,c :underline nil :foreground ,cyan-faint))) 64 | `(jf/org-faces-epigraph 65 | ((,c :underline nil :slant oblique :foreground ,fg-alt))) 66 | `(jf/org-faces-abbr 67 | ((,c :underline t :slant oblique :foreground ,fg-dim))) 68 | `(org-list-dt 69 | ((,c :bold t :slant italic :foreground ,fg-alt))) 70 | `(font-lock-misc-punctuation-face 71 | ((,c :foreground ,green-warmer))) 72 | `(elixir-ts-comment-doc-identifier 73 | ((,c :foreground ,comment))) 74 | `(elixir-ts-comment-doc-attribute 75 | ((,c :foreground ,comment))) 76 | ;; `(mode-line 77 | ;; ((,c :foreground ,cyan :background ,bg-cyan-subtle))) 78 | `(org-block 79 | ;; ((,c :background ,bg-yellow-subtle))) 80 | ((,c :background ,bg-added-faint))) 81 | `(org-block-begin-line 82 | ((,c :background ,bg-added-refine))) 83 | `(org-block-end-line 84 | ((,c :background ,bg-added-refine))) 85 | `(org-modern-priority 86 | ((,c :foreground ,fg-term-red-bright 87 | :box (:color ,fg-term-red-bright :line-width (-1 . -1))))) 88 | `(fill-column-indicator 89 | ((,c :width ultra-condensed 90 | :background ,bg-dim 91 | :foreground ,bg-dim))) 92 | `(font-lock-regexp-face 93 | ((,c :foreground ,red)))))) 94 | (setq jf/themes-plist '(:dark ef-bio :light ef-cyprus))) 95 | -------------------------------------------------------------------------------- /emacs.d/jf-gaming.el: -------------------------------------------------------------------------------- 1 | ;;; jf-gaming.el --- Gaming related functions -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2022 Jeremy Friesen 4 | ;; Author: Jeremy Friesen 5 | 6 | ;; This file is NOT part of GNU Emacs. 7 | ;;; Commentary: 8 | 9 | ;;; Code: 10 | 11 | ;;;; Dependencies 12 | (require 'jf-quick-help) 13 | 14 | ;;;; General 15 | 16 | (defconst (jf/gaming/runes 17 | '(("ᚠ" . "Gandalf Rune for the One Ring") ;; (Runic Letter Fehu Feoh Fe F) Gandalf rune 18 | ("Շ" . "Success Icon for the One Ring") ;; (Armenian Capital Letter Sha) Success Icon 19 | ("⏿" . "Eye of Sauron for the One Ring") ;; (Observer Eye Symbol) Sauron symbol 20 | ))) 21 | ;;;; Burning Wheel Code 22 | ;; (jf/minor-mode-maker :title "Burning Wheel Gold" 23 | ;; ;; Being a programmer and someone who plays table top 24 | ;; ;; role-playing games (TTRPG), I’ve often used the TTRPG 25 | ;; ;; rules-set or systems to explore programming languages 26 | ;; ;; and processes. After all, I understand the TTRPG rules 27 | ;; ;; well enough (or the algorithm’s description) that I can 28 | ;; ;; spend time thinking through my approach in a 29 | ;; ;; programming language. 30 | ;; :abbr "bwg" 31 | ;; :hooks (list 'org-mode-hook 'markdown-mode-hook)) 32 | 33 | (jf/transient-quick-help jf/bwg-qh-wises 34 | :label "Wises" 35 | :header "BWG Wises Obstacles (page 309)" 36 | :body 37 | (s-join 38 | "\n" 39 | '("Common knowledge .............. Ob 1" 40 | "An interesting fact ........... Ob 2" 41 | "Details ....................... Ob 3" 42 | "Uncommon knowledge ............ Ob 4" 43 | "Rare goods .................... Ob 5" 44 | "Bizarre or obscure ............ Ob 7" 45 | "Freaky details or specifics ... Ob 8"))) 46 | 47 | (jf/transient-quick-help jf/bwg-qh-expertise-exponent 48 | :label "Exponents" 49 | :header "BWG Expertise Exponent (page 12)" 50 | :body 51 | (s-join 52 | "\n" 53 | '("Exp 1 is naturally disinclined, crippled, or utterly incompetent." 54 | "Exp 2 is untrained, raw, weak, or unpracticed." 55 | "Exp 3 is nominally trained and practiced." 56 | "Exp 4 is competent; everday stuff doesn't pose a challenge." 57 | "Exp 5 is expert." 58 | "Exp 6 is near mastery." 59 | "Exp 7 is excellence." 60 | "Exp 8 is total mastery, complete understanding." 61 | "Exp 9 is uncanny; incomprehensibly good." 62 | "Exp 10 is as near perfection as the system allows."))) 63 | 64 | (jf/transient-quick-help jf/bwg-qh-absolute-difficulty 65 | :label "Difficulty" 66 | :header "BWG Absolute Difficulty (page 15)" 67 | :body 68 | (s-join 69 | "\n" 70 | '("Ob 1 A simple act done with little thought." 71 | "Ob 2 An act performed routinely at your job." 72 | "Ob 3 An act you can accomplish if you concentrate." 73 | "Ob 4 A risky act." 74 | "Ob 5 An act that requires expertise." 75 | "Ob 6 An act that requires a heroic effort." 76 | "Ob 7 An improbable feat." 77 | "Ob 8 An act requiring preternatural ability or a lot of help." 78 | "Ob 9 An act deemed nearly impossible." 79 | "Ob 10 A miracle."))) 80 | 81 | (jf/transient-quick-help jf/bwg-qh-circles-obstacles 82 | :label "Circles" 83 | :header "BWG Circles Obstacles (page 380-381)" 84 | :body 85 | (s-join 86 | "\n" 87 | '("Occupation" 88 | " Broad occupation/profession, same life path ... +0 Ob" 89 | " Uncommon occupation, or within same setting ... +2 Ob" 90 | " Specific occupation, rare/unique occupation ... +3 Ob" 91 | "" 92 | "Station" 93 | " Same station .................................. +0 Ob" 94 | " Lower rank, station, or class ................. +1 Ob" 95 | " Higher rank, station, or class ................ +2 Ob" 96 | " Highest station or rank in the setting ........ +3 Ob" 97 | "" 98 | "Disposition and Knowledge" 99 | " Common to circle .............................. +0 Ob" 100 | " Different from circle members ................. +1-2 Ob" 101 | " Specific, detailed, or rare ................... +3 Ob" 102 | "" 103 | "Time and Place" 104 | " Doesn't matter ................................ +0 Ob" 105 | " Unusual for this character .................... +1-2 Ob" 106 | " Right here and now in the middle of trouble ... +3 Ob"))) 107 | 108 | (jf/transient-quick-help jf/bwg-qh-circles-alternate 109 | :header "BWG Circles 2006 (Burning Anthology p7)" 110 | :label "Circles 2006" 111 | :body 112 | (s-join 113 | "\n" 114 | '("Occupation" 115 | " Broad/common .................... +0 Ob" 116 | " Uncommon ........................ +2 Ob" 117 | " Specific/rare/unique ............ +3 Ob" 118 | "" 119 | "Station" 120 | " Same station .................... +0 Ob" 121 | " Higher or lower rank ............ +1 Ob" 122 | " Lowest .......................... +2 Ob" 123 | " Highest ......................... +3 Ob" 124 | "" 125 | "Attitude" 126 | " Neutral to PC ................... +0 Ob" 127 | " Predisposed or opposed .......... +1 Ob" 128 | " Proponent, loyal, or specific ... +3 Ob" 129 | "" 130 | "Knowledge" 131 | " Unimportant ..................... +0 Ob" 132 | " General for subject ............. +1 Ob" 133 | " Specific subject ................ +3 Ob" 134 | "" 135 | "Skill" 136 | " Typical (Exponent 3) ............ +0 Ob" 137 | " Competent (Exponent 4) .......... +1 Ob" 138 | " Expert (Expoonent 5) ............ +2 Ob" 139 | " Master (Exponent 6) ............. +3 Ob" 140 | "" 141 | "Place/Time" 142 | " Prior to important test ......... +0 Ob" 143 | " Prior to conflict ............... +1 Ob" 144 | " In midst of conflict ............ +3 Ob"))) 145 | 146 | (jf/transient-quick-help jf/bwg-qh-steel-test-adjustments 147 | :header "BWG Steel Test Adjustments (page 363)" 148 | :label "Steel" 149 | :body 150 | (s-join 151 | "\n" 152 | '("Conditions for Steel Advantags" 153 | " Being startled by something mundane ......... +2D" 154 | " Feeling safe in a group of friends/allies ... +1D" 155 | "" 156 | "Conditions for Steel Disadvantages" 157 | " Being shot at ............................... +1 Ob" 158 | " Being directly affect by magic .............. +1 Ob" 159 | " Witnessing a person killed .................. +1 Ob" 160 | " Small explosions ............................ +2 Ob" 161 | " Committing murder ........................... +2 Ob" 162 | " Explosions .................................. +3 Ob" 163 | " Witnessing pronounced sorcery at play ....... +3 Ob" 164 | " Seeing a ghost .............................. +3 Ob" 165 | " Seeing the living dead ...................... +4 Ob" 166 | " Volcanic eruptions, cataclysm ............... +4 Ob" 167 | " Seeing horrible magic at work ............... +4 Ob" 168 | " Being in the presence of the supernatural ... +5 Ob"))) 169 | 170 | (jf/transient-quick-help jf/bwg-qh-test-difficulty 171 | :header "BWG Difficulty of Test by Dice Rolled (p41)" 172 | :label "Test Difficulty" 173 | :body 174 | (s-join 175 | "\n" 176 | '("| Dice | Routine | Difficult | Challenging | 177 | |------+---------+-----------+-------------| 178 | | 1D | Ob 1 | Ob 1 | Ob 2+ | 179 | | 2D | Ob 1 | Ob 2 | Ob 3+ | 180 | | 3D | Ob 1-2 | Ob 3 | Ob 4+ | 181 | | 4D | Ob 1-2 | Ob 3-4 | Ob 5+ | 182 | | 5D | Ob 1-3 | Ob 4-5 | Ob 6+ | 183 | | 6D | Ob 1-4 | Ob 5-6 | Ob 7+ | 184 | | 7D | Ob 1-4 | Ob 5-7 | Ob 8+ | 185 | | 8D | Ob 1-5 | Ob 6-8 | Ob 9+ | 186 | | 9D | Ob 1-6 | Ob 7-9 | Ob 10+ | 187 | | 10D | Ob 1-7 | Ob 8-10 | Ob 11+ | 188 | | 11D | Ob 1-8 | Ob 9-11 | Ob 12+ | 189 | | 12D | Ob 1-9 | Ob 10-12 | Ob 13+ | 190 | | 13D | Ob 1-10 | Ob 11-13 | Ob 14+ | 191 | | 14D | Ob 1-11 | Ob 12-14 | Ob 15+ | 192 | | 15D | Ob 1-12 | Ob 13-15 | Ob 16+ | 193 | | 16D | Ob 1-13 | Ob 14-16 | Ob 17+ | 194 | | 17D | Ob 1-14 | Ob 15-17 | Ob 18+ | 195 | | 18D | Ob 1-15 | Ob 16-18 | Ob 19+ |"))) 196 | 197 | (defconst jf/bwg-mortal-wounds-scale 198 | ;; When running Burning Wheel Gold, on occassion I need to establish the PTGS 199 | ;; for a creature or person. 200 | ;; 201 | ;; Yes, I could’ve written out (0 "B1" "B2" "B3" "B4" "B5" "B6" "B7" "B8" "B9" 202 | ;; "B10" "B11" "B12" "B13" "B14" "B15" "B16" "G1" "G2" "G3" "G4" "G5" "G6" 203 | ;; "G7" "G8" "G9" "G10" "G11" "G12" "G13" "G14" "G15" "G16" "W1" "W2" "W3" 204 | ;; "W4" "W5" "W6" "W7" "W8" "W9" "W10" "W11" "W12" "W13" "W14" "W15" "W16") 205 | ;; faster than the following constant, but I wanted to learn a bit of 206 | ;; emacs-lisp, so I chose to write the following. 207 | 208 | ;; I copied that text string from the introspected variable. Because if I 209 | ;; wasn't going to write it the first time, I sure wasn't going to do it if I 210 | ;; had already stored that value in a constant. 211 | (let* ((shades '("B" "G" "W")) 212 | (rank '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16)) 213 | (scale (flatten-list 214 | (seq-map (lambda (s) 215 | (seq-map (lambda (r) 216 | (format "%s%s" s r)) 217 | rank)) 218 | shades)))) 219 | ;; I want B1 to have an index of 1. Hence pre-pending the 0 to the list. 220 | (add-to-list 'scale 0) 221 | scale) 222 | "The BWG Physical Tolerance Grayscale (from B1 to W16).") 223 | 224 | (defun jf/bwg-qh-ptgs (forte power &optional round-up) 225 | "Calculate Burning Wheel PTGS. 226 | 227 | This requires FORTE, POWER, and ROUND-UP. 228 | 229 | Note, this assumes Black or Grey shade only." 230 | (interactive "sForte: \nsPower: \nP") 231 | ;; Note the / function uses integer floor division. 232 | (let* ((forte-index (-elem-index (upcase forte) jf/bwg-mortal-wounds-scale)) 233 | (power-index (-elem-index (upcase power) jf/bwg-mortal-wounds-scale))) 234 | (unless forte-index (throw 'invalid-entry (format "Invalid Forte %s" forte))) 235 | (unless power-index (throw 'invalid-entry (format "Invalid Power %s" power))) 236 | (let ((scale (cond ((and (< forte-index 17) (< power-index 17)) 237 | (jf/bwg-qh-ptgs--shade-black forte-index 238 | power-index 239 | round-up)) 240 | ((and (< forte-index 17) (< power-index 34)) 241 | (jf/bwg-qh-ptgs--shade-black forte-index 242 | (- power-index 16) 243 | round-up 244 | :add 2)) 245 | ((and (< forte-index 34) (< power-index 17)) 246 | (jf/bwg-qh-ptgs--shade-black (- forte-index 16) 247 | power-index 248 | round-up 249 | :add 2)) 250 | ((and (< forte-index 34) (< power-index 34)) 251 | (jf/bwg-qh-ptgs--shade-gray forte-index 252 | power-index 253 | round-up))))) 254 | (jf/bwg-qh-ptgs--render-popup :power power 255 | :forte forte 256 | :round-up round-up 257 | :scale scale)))) 258 | 259 | (cl-defun jf/bwg-qh-ptgs--render-popup (&key power forte round-up scale) 260 | "Render PTGS help for given POWER, FORTE, ROUND-UP, and SCALE. 261 | 262 | Where SCALE is a list of 6 elements: Su, Li, Mi, Se, Tr, and Mo" 263 | (jf/quick-help 264 | :header (concat "BWG PTGS (p98, p546) Forte:" 265 | forte ", Power: " power (when round-up " (Rounded up)")) 266 | :body (format (concat 267 | "Su %s; Li %s; Mi %s; Se %s; Tr %s; Mo %s" 268 | "\n\nForte: %s\nPower: %s" 269 | (when round-up "\n(Rounded up)") "\n") 270 | (nth (nth 0 scale) jf/bwg-mortal-wounds-scale) 271 | (nth (nth 1 scale) jf/bwg-mortal-wounds-scale) 272 | (nth (nth 2 scale) jf/bwg-mortal-wounds-scale) 273 | (nth (nth 3 scale) jf/bwg-mortal-wounds-scale) 274 | (nth (nth 4 scale) jf/bwg-mortal-wounds-scale) 275 | (nth (nth 5 scale) jf/bwg-mortal-wounds-scale) 276 | (upcase forte) 277 | (upcase power)))) 278 | 279 | (cl-defun jf/bwg-qh-ptgs--shade-black (forte power 280 | &optional round-up 281 | &key (add 0)) 282 | "Calculate Black shade PTGS (BWGR p98, p546). 283 | 284 | This requires FORTE, POWER, ROUND-UP, and ADD. 285 | 286 | Returns a list of 6 elements: Su, Li, Mi, Se, Tr, and Mo" 287 | (interactive "nForte: \nnPower: \nP") 288 | ;; Note the / function uses integer floor division. 289 | (let* ((mw-rnd (if round-up 1 0)) 290 | (mo (+ 6 add (/ (+ forte power mw-rnd) 2))) 291 | (step (+ (/ (+ 1 forte) 2))) 292 | (su (+ 1 (/ forte 2))) 293 | (li (+ su step)) 294 | (mi (+ li step)) 295 | (se (+ mi step)) 296 | (tr (+ se step)) 297 | (tr (if (< tr mo) tr (- mo 1))) 298 | (se (if (< se tr) se (- tr 1))) 299 | (mi (if (< mi se) mi (- se 1))) 300 | (li (if (< li mi) li (- mi 1)))) 301 | (list su li mi se tr mo))) 302 | 303 | (defun jf/bwg-qh-ptgs--shade-gray (forte power &optional round-up) 304 | "Calculate Gray shade PTGS (BWGR p546). 305 | 306 | This requires FORTE, POWER, and ROUND-UP. 307 | 308 | Returns a list of 6 elements: Su, Li, Mi, Se, Tr, and Mo" 309 | (let* ((fort-exponent (- forte 16)) 310 | (mw-rnd (if round-up 1 0)) 311 | (mo (+ 6 (/ (+ forte power mw-rnd) 2))) 312 | (su fort-exponent) 313 | (li (+ su fort-exponent)) 314 | (mi (- mo 4)) 315 | (se (- mo 2)) 316 | (tr (- mo 1))) 317 | (list su li mi se tr mo))) 318 | 319 | (defconst jf/bwg-lifepath--path-to-html-file 320 | ;; While running (or playing) a game of Burning Wheel, it can be useful to have 321 | ;; access to character lifepaths. These can give you insight into a quick NPC. 322 | ;; 323 | ;; I have transformed and edited the http://charred-black.herokuapp.com/#/ into 324 | ;; individual YAML files that sit on my local machine. I also created a bit of 325 | ;; https://gohugo.io/ code to render lifepaths from those YAML files. You can 326 | ;; see an example at 327 | ;; https://takeonrules.com/2018/10/10/burning-wheel-lifepaths-inspired-by-warhammer-fantasy/ 328 | ;; 329 | ;; I’m thinking what would be useful to create a searchable index of those 330 | ;; lifepaths. For now, I’ll search based on the stock, setting, and lifepath 331 | ;; name (all of which happen to be in the pathname of the YAML file). 332 | ;; 333 | ;; But instead of hopping to the YAML file, I’d like to jump to the spot on an 334 | ;; HTML page with that information. This way when I “find” a lifepath, I can 335 | ;; see what other lifepaths are of comparable station (a common need when 336 | ;; testing Circles). 337 | "~/git/org/assets/burning-wheel.html" 338 | "The path to an HTML.") 339 | 340 | (defconst jf/bwg-lifepath--narrowing-regexp 341 | "data-lifepath=" 342 | "All lines in `jf/bwg-lifepath--path-to-html-file' that have this substring contain filterable data.") 343 | 344 | (keymap-global-set "C-M-s-b" 'jf/menu--bwg) 345 | (transient-define-prefix jf/menu--bwg () 346 | "Define the BWG help prefix." 347 | ["Burning Wheel" 348 | ("c" jf/bwg-qh-circles-obstacles) 349 | ("C" jf/bwg-qh-circles-alternate) 350 | ("d" jf/bwg-qh-absolute-difficulty) 351 | ("e" jf/bwg-qh-expertise-exponent) 352 | ("p" "PTGS" jf/bwg-qh-ptgs) 353 | ("s" jf/bwg-qh-steel-test-adjustments) 354 | ("t" jf/bwg-qh-test-difficulty) 355 | ("w" jf/bwg-qh-wises) 356 | ]) 357 | 358 | ;;;; Core RPG 359 | 360 | ;; (jf/minor-mode-maker :title "CORE RPG" 361 | ;; :abbr "core" 362 | ;; :hooks (list 'org-mode-hook 'markdown-mode-hook)) 363 | 364 | (jf/transient-quick-help jf/rpg-core-qh-stat-scores 365 | :label "Stat Descriptors" 366 | :header "CORE Stat Descriptors" 367 | :body 368 | (s-join 369 | "\n" 370 | '("1 ... Average" 371 | "2 ... Remarkable" 372 | "3 ... Excellent" 373 | "4 ... Gifted" 374 | "5 ... Prodigy" 375 | "6 ... Apex"))) 376 | 377 | (jf/transient-quick-help jf/rpg-core-qh-skill-scores 378 | :label "Skill Descriptors" 379 | :header "CORE Skill Descriptors" 380 | :body 381 | (s-join 382 | "\n" 383 | '("1 ... Trained" 384 | "2 ... Competent" 385 | "3 ... Veteran" 386 | "4 ... Expert" 387 | "5 ... Innovator" 388 | "6 ... Legend"))) 389 | 390 | (jf/transient-quick-help jf/rpg-core-qh-item-bonuses 391 | :label "Item Bonus Levels" 392 | :header "CORE Item Bonus Levels" 393 | :body 394 | (s-join 395 | "\n" 396 | '("1 ... Trained" 397 | "2 ... Competent" 398 | "3 ... Veteran" 399 | "4 ... Expert" 400 | "5 ... Innovator" 401 | "6 ... Legend"))) 402 | 403 | (jf/transient-quick-help jf/rpg-core-qh-fame-levels 404 | :label "Fame Levels" 405 | :header "CORE Fame Levels" 406 | :body 407 | (s-join 408 | "\n" 409 | '("1 ... Local/Professional" 410 | "2 ... Regional/Subcultural" 411 | "3 ... National/Cultural" 412 | "4 ... International/Global" 413 | "5 ... Historical/Legendary" 414 | "6 ... Mythic Universal"))) 415 | 416 | (jf/transient-quick-help jf/rpg-core-qh-difficulty-levels 417 | :label "Difficulty Levels (DL)" 418 | :header "CORE Difficulty Levels" 419 | :body 420 | (s-join 421 | "\n" 422 | '("1 .... No-Brainer" 423 | "2 .... Easy" 424 | "3 .... Challenging" 425 | "4 .... Difficulut" 426 | "5 .... Hard" 427 | "6 .... Very Hard" 428 | "7 .... Unlikely" 429 | "8 .... Ridiculous" 430 | "9 .... Absurd" 431 | "10 ... Insane"))) 432 | 433 | (jf/transient-quick-help jf/rpg-core-qh-difficulty-levels 434 | :label "Difficulty Levels (DL) (RMSS)" 435 | :header "CORE Difficulty Levels (RMSS)" 436 | :body 437 | (s-join 438 | "\n" 439 | '("1 .... Routine" 440 | "2 .... Easy" 441 | "3 .... Challenging" 442 | "4 .... Difficult" 443 | "5 .... Hard" 444 | "6 .... Very Hard" 445 | "7 .... Extermely Hard" 446 | "8 .... Sheer Folly" 447 | "9 .... Absurd" 448 | "10 ... Nigh Impossible"))) 449 | 450 | (jf/transient-quick-help jf/rpg-core-qh-standard-modifiers 451 | :label "Standard Modifiers" 452 | :header "CORE Standard Modifiers" 453 | :body 454 | (s-join 455 | "\n" 456 | '("+N .... Skill level and items" 457 | "+1 .... Superior position/advantage" 458 | "+1 .... Character development from /Flash of Insight/" 459 | "-1 .... Wounded for 2 or more" 460 | "-1 .... Within skill but not specialty" 461 | "-1 .... Pro kit required but makeshift tools" 462 | "-2 .... Pro kit required but no tools" 463 | "-2 .... Attempting 2 Actions at once"))) 464 | 465 | (jf/transient-quick-help jf/rpg-core-qh-hit-locations 466 | :label "Hit Locations" 467 | :header "CORE Hit Locations; +2 to attacks from above." 468 | :body 469 | (s-join 470 | "\n" 471 | '("2 .... Left Leg" 472 | "3 .... Right Leg" 473 | "4 .... Crotch/Abdoment" 474 | "5 .... Left Arm" 475 | "6 .... Right Arm" 476 | "7 .... Belly/Lower Back" 477 | "8 .... Left Shoulder" 478 | "9 .... Right Shoulder" 479 | "10 ... Chest/Upper Back" 480 | "11 ... Neck" 481 | "12 ... Head"))) 482 | 483 | (jf/transient-quick-help jf/rpg-core-qh-lifeshaping-events 484 | :label "LifeShaping Events" 485 | :header "CORE LifeShaping Events (DayTrippers)" 486 | :body 487 | (s-join 488 | "\n" 489 | '("Belief ......... What the PC Believes" 490 | "Concept ........ What Ideas the PC Has" 491 | "Duty ........... What the PC Is Obliged to Do" 492 | "Goal ........... What the PC Wants to Do" 493 | "History ........ What the PC Has Learned in Life" 494 | "Mission ........ What the PC’s Orders Are" 495 | "Problem ........ Stuff the PC Has Issues With" 496 | "Relationship ... People the PC Interacts With" 497 | "Thing .......... The PC’s Most Personal Possessions"))) 498 | 499 | (jf/transient-quick-help jf/rpg-core-qh-help 500 | :label "Help" 501 | :header "CORE Help (DayTrippers)" 502 | :body 503 | (s-join 504 | "\n" 505 | '("Miss 2 or more ... Help gets -1"))) 506 | 507 | ;;;; Eberron 508 | ;; (jf/minor-mode-maker :title "Eberron" 509 | ;; :abbr "eb" 510 | ;; :hooks (list 'org-mode-hook 'markdown-mode-hook)) 511 | 512 | (jf/transient-quick-help jf/eberron-qh-dragonmarks 513 | :label "Dragonmarks" 514 | :header "Eberron Dragonmarks, Houses, and Stock" 515 | :body 516 | (s-join 517 | "\n" 518 | '("| Name | House | Stock |" 519 | "|------------------+---------------------+-----------------------|" 520 | "| Detection | Medani | half-elf |" 521 | "| Finding | Tharashk | half-orc, human |" 522 | "| Handling | Vadalis | human |" 523 | "| Healing | Jorasco | halfling |" 524 | "| Hospitality | Ghallanda | halfling |" 525 | "| Making | Cannith | human |" 526 | "| Passage | Orien | human |" 527 | "| Scribing | Sivis | gnome |" 528 | "| Sentinel | Deneith | human |" 529 | "| Shadow | Phiarlan & Thuranni | elf |" 530 | "| Storm | Lyrandar | half-elf |" 531 | "| Warding | Kundarak | dwarf |" 532 | "| Death | Vol | elf, currently lost |" 533 | "| Aberrant | Tarkanan | any |"))) 534 | 535 | (jf/transient-quick-help jf/eberron-qh-planes 536 | :label "Planes" 537 | :header "Eberron Planes" 538 | :body 539 | (s-join 540 | "\n" 541 | '("Daanvi, the Perfect Order" 542 | "Dal Quor, the Region of Dreams" 543 | "Dolurrh, the Realm of the Dead" 544 | "Fernia, the Sea of Fire" 545 | "Irian, the Eternal Day" 546 | "Kythri, the Churning Chaos" 547 | "Lamannia, the Twilight Forest" 548 | "Mabar, the Endless Night" 549 | "Risia, the Plain of Ice" 550 | "Shavarath, the Battleground" 551 | "Syrania, the Azure Sky" 552 | "Thelanis, the Faerie Court" 553 | "Xoriat, the Realm of Madness"))) 554 | 555 | (jf/transient-quick-help jf/eberron-qh-religion 556 | :label "Religion" 557 | :header "Eberron Religion" 558 | :body 559 | (s-join 560 | "\n" 561 | '("| Pantheon | Deity | Domain |" 562 | "|--------------+-------------------+-------------------|" 563 | "| Sovereign | Arawai | Life, Love |" 564 | "| Sovereign | Aureon | Law, Lore |" 565 | "| Sovereign | Balinor | Horn, Hunt |" 566 | "| Sovereign | Boldrei | Hall, Hearth |" 567 | "| Sovereign | Dol Arrah | Sun, Sacrifice |" 568 | "| Sovereign | Dol Dorn | Strength, Steel |" 569 | "| Sovereign | Kol Korran | World, Wealth |" 570 | "| Sovereign | Olladra | Feast, Fortune |" 571 | "| Sovereign | Onatar | Fire, Forge |" 572 | "| Silver Flame | Silver Flame, the | Goodness, Law |" 573 | "| Dark Six | Devourer, the | Wave, Whelm |" 574 | "| Dark Six | Fury, the | Rage, Ruin |" 575 | "| Dark Six | Keeper, the | Death, Decay |" 576 | "| Dark Six | Mockery, the | Betray, Bloodshed |" 577 | "| Dark Six | Shadow, the | Magic, Mayhem |" 578 | "| Dark Six | Traveler, the | Chaos, Change |"))) 579 | 580 | (transient-define-prefix jf/menu--eberron () 581 | "Define the Eberron help prefix." 582 | ["Eberron" 583 | ("d" jf/eberron-qh-dragonmarks) 584 | ("p" jf/eberron-qh-planes) 585 | ("r" jf/eberron-qh-religion) 586 | 587 | ]) 588 | (provide 'jf-gaming) 589 | ;;; jf-gaming.el ends here 590 | -------------------------------------------------------------------------------- /emacs.d/jf-graveyard.org: -------------------------------------------------------------------------------- 1 | Welcome to the graveyard; I'm not ready to fully say good-bye, but these are not part of my required code. 2 | 3 | ** My Collection of Org Roam interactions 4 | :PROPERTIES: 5 | :ID: C5FFA2DD-B321-4579-8D85-6F50C854A1EF 6 | :END: 7 | 8 | The =node-id= below is an [[denote:20221009T115044][Org-Roam]] node, something I’ve moved away from in favor of Denote. My reason for keeping this is a reminder of the =seq-mapcat= function. 9 | 10 | #+begin_src emacs-lisp 11 | (defun jf/blockquote-hugo (node-id) 12 | "Export the blockquote for the given NODE-ID" 13 | (let ((data (jf/org-mode-extract-body-and-properties node-id))) 14 | (concat 15 | "\n{{{< blockquote " (jf/hugo-blockquote-attributes-for (plist-get data :properties)) ">}}}\n" 16 | (format "%s" (plist-get data :body)) 17 | "\n{{{< /blockquote >}}}\n"))) 18 | 19 | (defun jf/hugo-blockquote-attributes-for (properties) 20 | "Map the PROPERTIES to attributes." 21 | (seq-mapcat (lambda (element) 22 | (let ((key (car element)) 23 | (text (cadr element))) 24 | (pcase key 25 | ("ID" (format "orgId=\"%s\" " text)) 26 | ("TITLE" (format "cite=\"%s\" " text)) 27 | ("CITE_URL" (format "citeUrl=\"%s\" " text)) 28 | ("AUTHOR" (format "pre=\"%s\" " text)) 29 | ("CITE_POST" (format "post=\"%s\" " text)) 30 | (_ "")))) 31 | properties)) 32 | #+end_src 33 | 34 | Related are the following commands: 35 | 36 | #+begin_src emacs_lisp 37 | (cl-defun jf/org-mode-extract-body-and-properties (node-id) 38 | "Extract quotable body and properties from NODE-ID." 39 | (with-current-buffer (find-file-noselect (org-id-find-id-file node-id)) 40 | (list :properties (org-element-map (org-element-parse-buffer 'object) 41 | '(keyword node-property) 42 | #'jf/org-mode-get-keyword-key-value) 43 | :body (jf/org-mode-extract-body-from-current-buffer)))) 44 | 45 | 46 | (defun jf/org-mode-extract-body-from-current-buffer () 47 | "Extract the body from the current org-mode body" 48 | (buffer-substring (save-excursion 49 | (jf/org-mode-find-point-that-starts-body t) 50 | (point)) 51 | (org-entry-end-position))) 52 | 53 | (defun jf/org-mode-find-point-that-starts-body (&optional unsafe) 54 | "Skip headline, planning line, and all drawers in current entry. 55 | If UNSAFE is non-nil, assume point is on headline." 56 | (unless unsafe 57 | ;; To improve performance in loops (e.g. with `org-map-entries') 58 | (org-back-to-heading)) 59 | (cl-loop for element = (org-element-at-point) 60 | for pos = (pcase element 61 | (`(headline . ,_) (org-element-property :contents-begin element)) 62 | (`(,(or 'planning 'property-drawer 'node-property 'keyword 'drawer) . ,_) (org-element-property :end element))) 63 | while pos 64 | do (goto-char pos))) 65 | #+end_src 66 | 67 | ** Logic for Parsing My Local Post 68 | :PROPERTIES: 69 | :ID: 60E9FC3A-9E31-4D02-B67D-11897671905E 70 | :END: 71 | 72 | #+begin_src emacs-lisp 73 | (cl-defun jf/tor-view-blog-post (&key 74 | (hostname jf/tor-hostname-current)) 75 | "Browse the url for the HOSTNAME 76 | 77 | The front matter of blog posts contains YAML, with two 78 | attributes: slug and date. Based on the site configuration, the 79 | URLs for one of those posts is: hostname/year/month/day/slug" 80 | (interactive) 81 | (let ((slugs)) 82 | (save-excursion 83 | ;; Remember we are making a list and pushing to the beginning of 84 | ;; the list. Hence we start with the last slug in mind. 85 | (goto-char 1) 86 | (re-search-forward "^slug: \\(.*\\)$" nil t) 87 | (push (match-string 1) slugs) 88 | (goto-char 1) 89 | (re-search-forward 90 | "^date: \\([[:digit:]]+\\)-\\([[:digit:]]+\\)-\\([[:digit:]]+\\) " 91 | nil t) 92 | ;; Then move to day, month, then year. 93 | (push (match-string 3) slugs) 94 | (push (match-string 2) slugs) 95 | (push (match-string 1) slugs) 96 | ;; And finally the host name. 97 | (push hostname slugs)) 98 | (browse-url (format "%s" (s-join "/" slugs))))) 99 | #+end_src 100 | 101 | ** Finding Draft File 102 | :PROPERTIES: 103 | :ID: F57621D5-9C92-47FD-8B28-B29300D82D29 104 | :END: 105 | 106 | #+begin_src emacs-lisp 107 | (defun jf/tor-find-file-draft (filename) 108 | "Find a draft FILENAME in the TakeOnRules content directory." 109 | (interactive 110 | (list (jf/find-file-via-matching 111 | :prompt "Draft filename: " 112 | :matching "^draft: true" 113 | :in (f-join jf/tor-home-directory "content")))) 114 | (find-file filename)) 115 | #+end_src 116 | 117 | ** Automation Now Favored By Denote 118 | :PROPERTIES: 119 | :ID: E61F385C-8B0B-44A1-893A-7E3457B95CD4 120 | :END: 121 | 122 | A massive function for helping make the preliminaries of a blog post. 123 | 124 | #+begin_src emacs-lisp 125 | (cl-defun jf/tor-post---create-or-append (&key 126 | title subheading 127 | (tags '("null")) series toc 128 | citeTitle citeURL citeAuthor) 129 | "Create or append a post with TITLE. 130 | 131 | The following keys are optional: 132 | 133 | :SUBHEADING if you have an active region, use this header. 134 | :TAGS one or more tags, as a list or string, to add to the 135 | frontmatter. 136 | :SERIES the series to set in the frontmatter. 137 | :TOC whether to include a table of contents in the post. 138 | :CITETITLE the title of the URL cited (if any) 139 | :CITEURL the URL cited (if any) 140 | :CITEAUTHOR the author cited (if any) 141 | 142 | If there's an active region, select that text and place it." 143 | (let* ((default-directory (f-join jf/tor-home-directory 144 | "content" "posts" 145 | (format-time-string "%Y/"))) 146 | 147 | (slug (denote-sluggify title)) 148 | (fpath (expand-file-name 149 | (concat default-directory slug ".md")))) 150 | ;; If the file does not exist, create the file with the proper 151 | ;; frontmatter. 152 | (if (not (file-exists-p fpath)) 153 | (write-region 154 | (concat "---" 155 | "\ndate: " (format-time-string "%Y-%m-%d %H:%M:%S %z") 156 | "\ndraft: true" 157 | "\nlayout: post" 158 | "\nlicenses:\n- all-rights-reserved" 159 | "\nslug: " (format "%s" slug) 160 | "\ntitle: '" (jf/tor-convert-text-to-post-title title) "'" 161 | "\ntype: post" 162 | (when series (concat "\nseries: " series)) 163 | (when toc (concat "\ntoc: true")) 164 | "\ntags:" 165 | (if tags 166 | (concat (mapconcat 167 | (lambda (tag) (concat "\n- " tag)) 168 | (flatten-tree tags) "")) 169 | "\n- null") 170 | "\n---\n") 171 | nil fpath)) 172 | ;; If we have an active region, append that region's content to 173 | ;; the given file. 174 | (if (use-region-p) 175 | (write-region 176 | (concat 177 | (if subheading 178 | (concat "\n## " subheading "\n") 179 | (when citeTitle (concat "\n## " citeTitle "\n"))) 180 | (when citeURL (concat 181 | "\n{{< blockquote" 182 | (when citeAuthor 183 | (concat " pre=\"" citeAuthor "\"")) 184 | " cite=\"" citeTitle 185 | "\" cite_url=\"" citeURL "\" >}}\n")) 186 | (buffer-substring (region-beginning) (region-end)) 187 | (when citeURL "\n{{< /blockquote >}}")) 188 | nil fpath t) 189 | ;; Without an active region, if we have a citeURL insert a link 190 | ;; to it. 191 | (when citeURL 192 | (write-region 193 | (concat 194 | "\n" 196 | (or (citeTitle) (citeURL)) "\n") 197 | nil fpath t))) 198 | ;; Finally open that file for editing. 199 | (find-file fpath) 200 | (end-of-buffer))) 201 | #+end_src 202 | 203 | 204 | ** Some Org Roam Note Exporting 205 | :PROPERTIES: 206 | :ID: 92361757-90E8-4053-81BA-AF2C1BD21340 207 | :END: 208 | 209 | #+begin_src emacs-lisp 210 | (cl-defun jf/org-markdown-export-format-link-for (&key node desc) 211 | "Return a \"link\" text based on the given NODE and DESC. 212 | 213 | This relates to my glossary.html Hugo short-code." 214 | (when-let (url (jf/org-roam-external-url-for :node node)) 215 | (let ((key (jf/org-roam-node-get-org-mode-property :node node :property "GLOSSARY_KEY"))) 216 | (cond 217 | ((jf/org-roam-node-get-org-mode-property :node node :property "OFFER") 218 | (format "{{< glossary key=\"%s\" >}}" key)) 219 | ((jf/org-roam-node-get-org-mode-property :node node :property "SAME_AS") 220 | (format "{{< glossary key=\"%s\" link=\"sameAs\" >}}" key)) 221 | (t (format "[%s](%s)" desc url)))))) 222 | 223 | ;;; For testing: 224 | ;; 225 | ;; (message "%s" (jf/org-markdown-export-format-link-for :node (org-roam-node-from-id "FC017488-D8EC-43DE-A35D-4D10A87B6A0D") :desc "Burning Wheel Gold")) 226 | ;; (message "%s" (jf/org-markdown-export-format-link-for :node (org-roam-node-from-id "86F3E44F-AA0E-4B08-B0D8-30A764B4CD13") :desc "Org Roam")) 227 | #+end_src 228 | 229 | ** Org Mode and Projects 230 | :PROPERTIES: 231 | :ID: 095A9509-CAA4-439E-8006-0F8152E13C46 232 | :END: 233 | 234 | These are functions I onced used for my project time tracking. What I have with dailies appears to be doing an adequate job. These are here for posterity. 235 | 236 | #+begin_src emacs-lisp 237 | ;; (defun jf/force-org-rebuild-cache (prefix-arg) 238 | ;; "Call functions to rebuild the applicable `org-mode' and `org-roam' cache(s). 239 | 240 | ;; When given PREFIX_ARG, clear the org-roam database (via 241 | ;; `org-roam-db-clear-all') then sync. This will slow down the sync." 242 | ;; (interactive "P") 243 | ;; (org-id-update-id-locations) 244 | ;; (when (fboundp 'org-roam-db-clear-all) 245 | ;; (progn 246 | ;; (when (car prefix-arg) (org-roam-db-clear-all)) 247 | ;; (org-roam-db-sync) 248 | ;; (org-roam-update-org-id-locations)))) 249 | 250 | (cl-defun jf/org-agenda/send-forward-task () 251 | "Send an `org-mode' task node forward." 252 | (interactive) 253 | (save-excursion 254 | (let* ((day-project-task 255 | (jf/org-agenda/timesheet/get-day-and-project-and-task-at-point)) 256 | (from-project (plist-get day-project-task :project)) 257 | (from-task (plist-get day-project-task :task))) 258 | ;; Narrowing the region to perform quicker queries on the element 259 | (narrow-to-region (org-element-property :begin from-task) 260 | (org-element-property :end from-task)) 261 | 262 | ;; Grab each section for the from-task and convert that into text. 263 | ;; 264 | ;; Yes we have the from-task, however, we haven't parsed that entity. 265 | ;; Without parsing that element, the `org-element-contents' returns nil. 266 | (let ((content (s-join "\n" (org-element-map (org-element-parse-buffer) 267 | 'section 268 | (lambda (section) 269 | (mapconcat 270 | (lambda (element) 271 | (pcase (org-element-type element) 272 | ;; I want to skip my time entries 273 | ('drawer nil) 274 | (_ (buffer-substring-no-properties 275 | (org-element-property 276 | :begin element) 277 | (org-element-property 278 | :end element))))) 279 | (org-element-contents section) 280 | "\n")))))) 281 | (widen) 282 | (org-capture-string (format "%s %s :%s:\n\n%s %s %s :%s:\n%s" 283 | (s-repeat (org-element-property :level from-project) "*") 284 | (org-element-property :raw-value from-project) 285 | (s-join ":" (org-element-property :tags from-project)) 286 | (s-repeat (org-element-property :level from-task) "*") 287 | (org-element-property :todo-keyword from-task) 288 | (org-element-property :raw-value from-task) 289 | (s-join ":" (org-element-property :tags from-task)) 290 | content) 291 | "d")) 292 | ;; Now that we've added the content, let's tidy up the from-task. 293 | (goto-char (org-element-property :contents-begin from-task)) 294 | ;; Prompt for the todo state of the original task. 295 | (call-interactively 'org-todo)))) 296 | 297 | (defun jf/org-agenda/timesheet/get-day-and-project-and-task-at-point () 298 | "Return a plist of :day, :project, and :task for element at point." 299 | (let* ((task (jf/org-agenda-headline-for-level :level 5)) 300 | (project (progn 301 | (org-up-heading-safe) 302 | (org-element-at-point))) 303 | (day (progn 304 | (org-up-heading-safe) 305 | (org-element-at-point)))) 306 | (list :project project :task task :day day))) 307 | 308 | (cl-defun jf/org-agenda-headline-for-level (&key (level 5)) 309 | "Find the `org-mode' ancestor headline with LEVEL." 310 | (let ((element (org-element-at-point))) 311 | (if (eq 'headline (org-element-type element)) 312 | (let ((element-level (org-element-property :level element))) 313 | (cond 314 | ((= level element-level) 315 | (progn (message "Found %s" element) element)) 316 | ((> level element-level) 317 | (user-error "Selected element %s is higher level." element-level)) 318 | ((< level element-level) 319 | (progn (org-up-heading-safe) (jf/org-agenda-headline-for-level :level level))))) 320 | (progn 321 | (org-back-to-heading) 322 | (jf/org-agenda-headline-for-level :level level))))) 323 | 324 | 325 | (defun jf/org-mode-agenda-project-prompt () 326 | "Prompt for project based on existing projects in agenda file. 327 | 328 | Note: I tried this as interactive, but the capture templates 329 | insist that it should not be interactive." 330 | (completing-read 331 | "Project: " 332 | (sort 333 | (seq-uniq 334 | (org-map-entries 335 | (lambda () 336 | (org-element-property :raw-value (org-element-at-point))) 337 | "+LEVEL=4+projects" 'agenda)) 338 | #'string<))) 339 | 340 | ;; When I jump to a new task for the day, I want to position that task within 341 | ;; the prompted project. Inspiration from 342 | ;; https://gist.github.com/webbj74/0ab881ed0ce61153a82e. 343 | (cl-defun jf/org-mode-agenda-find-project-node 344 | (&key 345 | (tag "projects") 346 | (project (jf/org-mode-agenda-project-prompt)) 347 | ;; The `file+olp+datetree` directive creates a headline like “2022-09-03 Saturday”. 348 | (within_headline (format-time-string "%Y-%m-%d %A"))) 349 | "Position `point' at the end of the given PROJECT WITHIN_HEADLINE. 350 | 351 | And use the given TAG." 352 | ;; We need to be using the right agenda file. 353 | (with-current-buffer (find-file-noselect 354 | jf/agenda-filename/local) 355 | (let ((existing-position (org-element-map 356 | (org-element-parse-buffer) 357 | 'headline 358 | ;; Finds the end position of: 359 | ;; - a level 4 headline 360 | ;; - that is tagged as a :projects: 361 | ;; - is titled as the given project 362 | ;; - and is within the given headline 363 | (lambda (hl) 364 | (and (=(org-element-property :level hl) 4) 365 | ;; I can't use the :title attribute as it 366 | ;; is a more complicated structure; this 367 | ;; gets me the raw string. 368 | (string= project 369 | (plist-get (cadr hl) :raw-value)) 370 | (member tag 371 | (org-element-property :tags hl)) 372 | ;; The element must have an ancestor with 373 | ;; a headline of today 374 | (string= within_headline 375 | (plist-get 376 | ;; I want the raw title, no 377 | ;; styling nor tags 378 | (cadr 379 | (car 380 | (org-element-lineage hl))) 381 | :raw-value)) 382 | (org-element-property :end hl))) 383 | nil t))) 384 | (if existing-position 385 | ;; Go to the existing position for this project 386 | (goto-char existing-position) 387 | (progn 388 | ;; Go to the end of the file and append the project to the end 389 | (goto-char (point-max)) 390 | ;; Ensure we have a headline for the given day 391 | (unless (org-element-map 392 | (org-element-parse-buffer) 393 | 'headline 394 | (lambda (hl) 395 | (string= within_headline 396 | (plist-get 397 | ;; I want the raw title, no styling nor tags 398 | (cadr (car (org-element-lineage hl))) 399 | :raw-value)))) 400 | (insert (concat "\n\n*** "within_headline))) 401 | (insert (concat "\n\n**** " project " :" tag ":\n\n"))))))) 402 | 403 | (cl-defun jf/org-mode-agenda-find-blocked-node () 404 | "Add a blocker node to today." 405 | (jf/org-mode-agenda-find-project-node :tag "blockers" 406 | :project (concat 407 | "Blockers for " 408 | (format-time-string 409 | "%Y-%m-%d")))) 410 | 411 | (cl-defun jf/org-mode-agenda-find-merge-request-node () 412 | "Add a mergerequest node to today." 413 | (jf/org-mode-agenda-find-project-node :tag "mergerequests" 414 | :project (concat "Merge Requests for " 415 | (format-time-string 416 | "%Y-%m-%d")))) 417 | 418 | ;; Takes my notes for the day and formats them for a summary report. 419 | (defun jf/org-mode-agenda-to-stand-up-summary (parg) 420 | "Copy to the kill ring the day's time-tracked summary. 421 | 422 | When given PARG, prompt for the day of interest. 423 | 424 | NOTE: This follows the convention that projects are on headline 4 and 425 | tasks within projects are headline 5." 426 | (interactive "P") 427 | (with-current-buffer (find-file-noselect 428 | jf/agenda-filename/local) 429 | (save-excursion 430 | (let ((within_headline 431 | ;; Use the CCYY-MM-DD Dayname format and prompt for a date if 432 | ;; PREFIX-ARG given. 433 | (format-time-string "%Y-%m-%d %A" 434 | (when (car parg) 435 | (org-read-date nil t nil "Pick a day:" ))))) 436 | (kill-new 437 | (concat "*Summary of " within_headline "*\n\n" 438 | (s-trim 439 | (s-join 440 | "\n" 441 | (org-element-map 442 | (org-element-parse-buffer) 443 | 'headline 444 | (lambda (hl) 445 | (when (member 446 | within_headline 447 | (mapcar 448 | (lambda (ancestor) 449 | (plist-get (cadr ancestor) :raw-value)) 450 | (org-element-lineage hl))) 451 | (pcase (org-element-property :level hl) 452 | (4 (concat "\n" (plist-get (cadr hl) :raw-value))) 453 | (5 (if (and 454 | (member "mergerequest" (org-element-property :tags hl)) 455 | (eq 'done (org-element-property :todo-type hl))) 456 | nil 457 | (concat "- " (plist-get (cadr hl) :raw-value)))) 458 | (_ nil))))))))) 459 | (jf/create-scratch-buffer) 460 | (yank))))) 461 | 462 | (defun jf/org-mode/narrow-to-date (date) 463 | "Narrow agenda to given DATE agenda subtree." 464 | (interactive (list (if current-prefix-arg 465 | (org-read-date nil nil nil "Pick a day:") 466 | (format-time-string "%Y-%m-%d")))) 467 | (widen) 468 | (goto-char (point-max)) 469 | (re-search-backward (concat "^\*\*\* " date)) 470 | (end-of-line) 471 | (org-narrow-to-subtree) 472 | (message "Narrowing to %s agenda" date)) 473 | 474 | ;; I’m responsible for tracking my work time. I want a way to quickly see what 475 | ;; that is for the current week. 476 | ;; 477 | ;; A utility function providing an overrview 478 | (cl-defun jf/org-mode-weekly-report () 479 | "Jump to my weekly time tracker. 480 | 481 | Useful for providing me with an overview of my total tracked time 482 | for the week." 483 | (interactive) 484 | (find-file jf/agenda-filename/local) 485 | (require 'pulsar) 486 | (pulsar-pulse-line) 487 | (org-clock-report 4)) 488 | 489 | ;; Another task at end of month is to transcribing my agenda’s timesheet to 490 | ;; entries in our time tracking software. From the day’s project link in the 491 | ;; =org-clock-report=, I want to copy the headlines of each of the tasks. I 492 | ;; fill out my time sheets one day at a time. 493 | (defun jf/org-mode-time-entry-for-project-and-day () 494 | "Function to help report time for Scientist.com. 495 | 496 | Assumes that I'm on a :projects: headline. 497 | 498 | - Sum the hours (in decimal form) for the tasks. 499 | - Create a list of the tasks. 500 | - Write this information to the message buffer. 501 | - Then move to the next heading level." 502 | (interactive) 503 | (let* ((project (plist-get (cadr (org-element-at-point)) :raw-value)) 504 | (tasks (s-join "\n" 505 | (org-with-wide-buffer 506 | (when (org-goto-first-child) 507 | (cl-loop collect (concat "- " 508 | (org-no-properties 509 | (org-get-heading t t t t))) 510 | while (outline-get-next-sibling)))))) 511 | (hours (/ (org-clock-sum-current-item) 60.0)) 512 | (output (format "Tasks:\n%s\nProject: %s\nHours: %s\n" 513 | tasks 514 | project 515 | hours))) 516 | (kill-new tasks) 517 | (message output))) 518 | #+end_src 519 | -------------------------------------------------------------------------------- /emacs.d/jf-minor-mode-maker.el: -------------------------------------------------------------------------------- 1 | ;;; jf-minor-mode-maker.el --- Simple focus mode and extras -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2022 Jeremy Friesen 4 | ;; Author: Jeremy Friesen 5 | 6 | ;; This file is NOT part of GNU Emacs. 7 | 8 | ;;; Commentary: 9 | 10 | ;; Provides a macro for personal minor mode declaration. Why the macro? As a 11 | ;; matter of practice and documentation. I also want to get better at writing. 12 | 13 | ;;; Code: 14 | (require 'cl-macs) 15 | (cl-defmacro jf/minor-mode-maker (&key title abbr hooks keymap) 16 | "A macro to declare a minor mode. 17 | 18 | Use TITLE to derive the docstring. 19 | Use ABBR to derive the mode-name lighter. 20 | Add hook to each HOOKS provided. 21 | And assign a KEYMAP." 22 | (let ((mode-name (intern (s-downcase (concat "jf/" abbr "-minor-mode")))) 23 | (lighter (concat " " abbr)) 24 | (docstring (concat "Minor mode for " title "."))) 25 | `(progn 26 | (define-minor-mode ,mode-name 27 | ,docstring 28 | :init-value nil 29 | :global nil 30 | :keymap ,keymap 31 | :lighter ,lighter) 32 | (when ,hooks 33 | (seq-each ,hooks (lambda(hook) (add-hook hook (lambda () (,mode-name))))))))) 34 | 35 | (provide 'jf-minor-mode-maker) 36 | ;;; jf-minor-mode-maker.el ends here 37 | -------------------------------------------------------------------------------- /emacs.d/jf-modus-themes.el: -------------------------------------------------------------------------------- 1 | (use-package modus-themes 2 | ;; I love [[http://protesilaos.com][Prot]]’s attention to detail with the modus 3 | ;; themes. Here’s my configuration for these two sibling themes. There’s a 4 | ;; bit of chatter, but all told it sets things up how I like. 5 | :straight (:type git :host gitlab :repo "protesilaos/modus-themes" :branch "main") 6 | :init 7 | (setq modus-themes-italic-constructs t 8 | modus-themes-bold-constructs t 9 | modus-themes-mixed-fonts t 10 | modus-themes-variable-pitch-ui nil 11 | modus-themes-custom-auto-reload t 12 | modus-themes-disable-other-themes t 13 | modus-themes-org-blocks 'tinted-background 14 | modus-themes-common-palette-overrides 15 | '( 16 | (comment yellow-faint) 17 | (string green) 18 | ;; Favoring more of the defaults; below is some settings I've used for 19 | ;; quite a while (paired with the above) 20 | ;; 21 | ;; (builtin magenta) 22 | ;; (constant magenta-cooler) 23 | ;; (docstring green-warmer) 24 | ;; (docmarkup magenta-faint) 25 | ;; (fnname magenta-warmer) 26 | ;; (keyword cyan) 27 | ;; (preprocessor cyan-cooler) 28 | ;; (type magenta-cooler) 29 | ;; (variable blue-warmer) 30 | ;; (rx-construct red-faint) 31 | ;; (rx-backslash blue-cooler) 32 | ) 33 | modus-themes-completions '((matches . (extrabold)) 34 | (selection . (semibold accented)) 35 | (popup . (accented intense))) 36 | modus-themes-headings 37 | '((1 . (variable-pitch bold 1.7)) 38 | (2 . (overline semibold 1.5)) 39 | (3 . (monochrome overline 1.4 background)) 40 | (4 . (overline 1.3)) 41 | (5 . (rainbow 1.2)) 42 | (6 . (rainbow 1.15)) 43 | (t . (rainbow 1.1))))) 44 | 45 | ;; And now the theme. I’ve chosen the modus themes (e.g. ~modus-vivendi~ and 46 | ;; ~modus-operandi~). They provide a light and dark theme with a focus on visual 47 | ;; accessibility. 48 | (defun jf/theme-custom-faces () 49 | "Set the various custom faces for both `treesit' and `tree-sitter'." 50 | (modus-themes-with-colors 51 | (custom-set-faces 52 | `(denote-faces-link 53 | ((,c (:inherit link 54 | :box (:line-width (1 . 1) 55 | :color ,border 56 | :style released-button))))) 57 | `(jf/bom-face 58 | ((,c (:width ultra-expanded :box (:line-width (2 . 2) 59 | :color ,underline-err 60 | :style released-button))))) 61 | `(jf/tabs-face 62 | ((,c :underline (:style wave :color ,bg-blue-intense)))) 63 | `(jf/org-faces-date 64 | ((,c :underline nil :foreground ,cyan-faint))) 65 | `(jf/org-faces-epigraph 66 | ((,c :underline nil :slant oblique :foreground ,fg-alt))) 67 | `(jf/org-faces-abbr 68 | ((,c :underline t :slant oblique :foreground ,fg-dim))) 69 | `(org-list-dt 70 | ((,c :bold t :slant italic :foreground ,fg-alt))) 71 | `(font-lock-misc-punctuation-face 72 | ((,c :foreground ,green-warmer))) 73 | `(org-block 74 | ;; ((,c :background ,bg-yellow-subtle))) 75 | ((,c :background ,bg-added-faint))) 76 | `(org-block-begin-line 77 | ((,c :background ,bg-added-refine))) 78 | `(org-block-end-line 79 | ((,c :background ,bg-added-refine))) 80 | `(hl-todo 81 | ((,c :foreground ,red-faint))) 82 | `(color-rg-font-lock-header-line-text 83 | ((,c :foreground ,green))) 84 | `(color-rg-font-lock-header-line-keyword 85 | ((,c :foreground ,keybind))) 86 | `(color-rg-font-lock-function-location 87 | ((,c :foreground ,keybind))) 88 | `(color-rg-font-lock-header-line-edit-mode 89 | ((,c :foreground ,keybind))) 90 | `(color-rg-font-lock-header-line-directory 91 | ((,c :foreground ,cyan :background ,bg-cyan-subtle))) 92 | `(color-rg-font-lock-command 93 | ((,c :foreground ,fg-alt :background ,bg-inactive))) 94 | `(color-rg-font-lock-file 95 | ((,c :foreground ,cyan :background ,bg-cyan-subtle))) 96 | `(color-rg-font-lock-line-number 97 | ((,c :background ,bg-dim :foreground ,fg-dim))) 98 | `(color-rg-font-lock-column-number 99 | ((,c :background ,bg-dim :foreground ,fg-dim))) 100 | `(color-rg-font-lock-position-splitter 101 | ((,c :background ,bg-dim :foreground ,fg-dim))) 102 | `(color-rg-font-lock-match 103 | ((,c :background ,cyan-cooler :foreground ,bg-cyan-subtle))) 104 | `(color-rg-font-lock-mark-changed 105 | ((,c :background ,bg-changed :foreground ,fg-changed))) 106 | `(color-rg-font-lock-mark-deleted 107 | ((,c :background ,bg-removed :foreground ,fg-removed))) 108 | ;; `(fill-column-indicator 109 | ;; ((,c :width ultra-condensed :background ,bg-dim :foreground ,bg-dim))) 110 | `(font-lock-regexp-face 111 | ((,c :foreground ,red)))))) 112 | 113 | (setq jf/themes-plist 114 | '(:dark modus-vivendi-tinted 115 | :light modus-operandi-tinted)) 116 | -------------------------------------------------------------------------------- /emacs.d/jf-project-theme-colors.el: -------------------------------------------------------------------------------- 1 | ;;; jf-project-theme-colors --- Consolidated colors for themes -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2023 Jeremy Friesen 4 | ;; Author: Jeremy Friesen 5 | 6 | ;; This file is NOT part of GNU Emacs. 7 | ;;; Commentary: 8 | 9 | ;; The options that I'm considering are from the modus color palette: 10 | ;; 11 | ;; bg-red-intense 12 | ;; bg-green-intense 13 | ;; bg-yellow-intense 14 | ;; bg-blue-intense 15 | ;; bg-magenta-intense 16 | ;; bg-cyan-intense 17 | ;; bg-red-subtle 18 | ;; bg-green-subtle 19 | ;; bg-yellow-subtle 20 | ;; bg-blue-subtle 21 | ;; bg-magenta-subtle 22 | ;; bg-cyan-subtle 23 | ;; bg-red-nuanced 24 | ;; bg-green-nuanced 25 | ;; bg-yellow-nuanced 26 | ;; bg-blue-nuanced 27 | ;; bg-magenta-nuanced 28 | ;; bg-cyan-nuanced 29 | ;; bg-ochre 30 | ;; bg-lavender 31 | ;; bg-sage 32 | 33 | ;;; Code: 34 | 35 | (require 'modus-themes) 36 | (require 'projectile) 37 | (defvar jf/project/theme-colors/table 38 | '(("~/git/dotemacs/" . bg-green-subtle) 39 | ("~/git/dotzshrc/" . bg-green-subtle) 40 | ("~/git/takeonrules.source/" . bg-green-nuanced) 41 | ("~/git/org/" . bg-green-nuanced) 42 | ("~/git/britishlibrary/" . bg-blue-intense) 43 | ("~/git/adventist-dl/" . bg-yellow-intense) 44 | ("~/git/utk-hyku/" . bg-red-intense) 45 | ("~/git/bulkrax/" . bg-sage) 46 | ("~/git/space_stone/" . bg-magenta-nuanced) 47 | ("~/git/derivative_rodeo/" . bg-ochre) 48 | ("~/git/hyrax/" . bg-sage)) 49 | "A list of projects and their colors. 50 | 51 | The `car' of each list item should be of begin with \"~/\" and 52 | end with \"/\" (so as to conform to multiple machines and 53 | projectile's interface.") 54 | 55 | (cl-defun jf/project/theme-colors/current (&key (default 'bg-blue-subtle)) 56 | "Return a HEX color (e.g. \"#CCDDEE\") for the given project. 57 | 58 | The DEFAULT is a named color in the `modus-themes' palette." 59 | 60 | (let* ((project-dir (abbreviate-file-name (or (projectile-project-root) "~/"))) 61 | (name (alist-get project-dir 62 | jf/project/theme-colors/table 63 | default nil #'string=))) 64 | (modus-themes-get-color-value name))) 65 | 66 | (defvar jf/project/theme-colors/faces 67 | (list 'line-number-current-line 'mode-line-active) 68 | "The faces to update with the theme-colors.") 69 | 70 | (defvar jf/project/theme-colors/hooks 71 | (list 'buffer-list-update-hook 72 | 'projectile-after-switch-project-hook) 73 | "The hooks to call to set the theme colors.") 74 | 75 | (defun jf/project/theme-colors/apply-to-buffer () 76 | "Apply the the project's colors to the buffer (e.g. 'mode-line-active)." 77 | (unless (active-minibuffer-window) 78 | (dolist (element jf/project/theme-colors/faces) 79 | (face-remap-add-relative 80 | element 81 | `( :background ,(jf/project/theme-colors/current) 82 | :foreground ,(face-attribute 'default :foreground)))))) 83 | 84 | ;; I need to ensure that I'm not doing this while Emacs is initializing. If I 85 | ;; don't have the 'after-init-hook I experience significant drag/failure to 86 | ;; initialize. 87 | (add-hook 'after-init-hook 88 | (lambda () 89 | (dolist (hook jf/project/theme-colors/hooks) 90 | (add-hook hook #'jf/project/theme-colors/apply-to-buffer)))) 91 | 92 | (provide 'jf-project-theme-colors) 93 | ;;; jf-project-theme-colors.el ends here 94 | -------------------------------------------------------------------------------- /emacs.d/jf-quick-help.el: -------------------------------------------------------------------------------- 1 | ;;; jf-quick-help.el --- Simple focus mode and extras -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2022 Jeremy Friesen 4 | ;; Author: Jeremy Friesen 5 | 6 | ;; This file is NOT part of GNU Emacs. 7 | ;; Package-Requires: ((transient "0.3.7") (emacs "25.1")) 8 | 9 | ;;; Commentary: 10 | ;; 11 | ;; This package provides a simple way to register quick help function. 12 | 13 | ;;; Code: 14 | (require 'cl-lib) 15 | (cl-defun jf/quick-help (&key header body) 16 | "Create a help window with HEADER and BODY." 17 | (let ((qh-buff (concat "*Quick Help: " header "*"))) 18 | (progn (or (get-buffer qh-buff) 19 | (progn (get-buffer-create qh-buff) 20 | (with-current-buffer qh-buff 21 | (insert (concat "**" header "**\n" body)) 22 | (goto-char (point-min)) 23 | (not-modified) 24 | (read-only-mode) 25 | (special-mode) 26 | (local-set-key (kbd "q") 'kill-buffer-and-window)))) 27 | (pop-to-buffer qh-buff '((display-buffer-below-selected) 28 | ;; When sizing the buffer, if tab-line-format is 29 | ;; active then that "line" is not counted in 30 | ;; calculating the fit-to-window-buffer value. 31 | ;; Which means that the buffer flows into the 32 | ;; mode-line. So turn it off for this buffer. 33 | (window-parameters . ((tab-line-format . none) 34 | (no-other-window . nil))) 35 | (window-height . fit-window-to-buffer))) 36 | (message "q - Remove Window")))) 37 | 38 | (cl-defmacro jf/transient-quick-help (name &key header label body) 39 | "Macro for creating callable functions that display help. 40 | 41 | NAME is name of function, 42 | LABEL is label for the menu 43 | HEADER is name of buffer, and TEXT is displayed." 44 | (declare (indent defun)) 45 | `(progn 46 | (transient-define-suffix ,name nil 47 | ,header 48 | :description ,label 49 | (interactive) 50 | (jf/quick-help :header ,header :body ,body)))) 51 | 52 | (provide 'jf-quick-help) 53 | ;;; jf-quick-help.el ends here 54 | -------------------------------------------------------------------------------- /emacs.d/jf-rubocop-cops.el: -------------------------------------------------------------------------------- 1 | (defvar jf/rubocop/list-all-cops 2 | ;; rubocop --show-cops | rg "^([A-Z][\w/]+):" -r '$1' | pbcopy 3 | '("Bundler/DuplicatedGem" 4 | "Bundler/GemComment" 5 | "Bundler/GemFilename" 6 | "Bundler/GemVersion" 7 | "Bundler/InsecureProtocolSource" 8 | "Bundler/OrderedGems" 9 | "Gemspec/DependencyVersion" 10 | "Gemspec/DeprecatedAttributeAssignment" 11 | "Gemspec/DevelopmentDependencies" 12 | "Gemspec/DuplicatedAssignment" 13 | "Gemspec/OrderedDependencies" 14 | "Gemspec/RequireMFA" 15 | "Gemspec/RequiredRubyVersion" 16 | "Gemspec/RubyVersionGlobalsUsage" 17 | "Layout/AccessModifierIndentation" 18 | "Layout/ArgumentAlignment" 19 | "Layout/ArrayAlignment" 20 | "Layout/AssignmentIndentation" 21 | "Layout/BeginEndAlignment" 22 | "Layout/BlockAlignment" 23 | "Layout/BlockEndNewline" 24 | "Layout/CaseIndentation" 25 | "Layout/ClassStructure" 26 | "Layout/ClosingHeredocIndentation" 27 | "Layout/ClosingParenthesisIndentation" 28 | "Layout/CommentIndentation" 29 | "Layout/ConditionPosition" 30 | "Layout/DefEndAlignment" 31 | "Layout/DotPosition" 32 | "Layout/ElseAlignment" 33 | "Layout/EmptyComment" 34 | "Layout/EmptyLineAfterGuardClause" 35 | "Layout/EmptyLineAfterMagicComment" 36 | "Layout/EmptyLineAfterMultilineCondition" 37 | "Layout/EmptyLineBetweenDefs" 38 | "Layout/EmptyLines" 39 | "Layout/EmptyLinesAroundAccessModifier" 40 | "Layout/EmptyLinesAroundArguments" 41 | "Layout/EmptyLinesAroundAttributeAccessor" 42 | "Layout/EmptyLinesAroundBeginBody" 43 | "Layout/EmptyLinesAroundBlockBody" 44 | "Layout/EmptyLinesAroundClassBody" 45 | "Layout/EmptyLinesAroundExceptionHandlingKeywords" 46 | "Layout/EmptyLinesAroundMethodBody" 47 | "Layout/EmptyLinesAroundModuleBody" 48 | "Layout/EndAlignment" 49 | "Layout/EndOfLine" 50 | "Layout/ExtraSpacing" 51 | "Layout/FirstArgumentIndentation" 52 | "Layout/FirstArrayElementIndentation" 53 | "Layout/FirstArrayElementLineBreak" 54 | "Layout/FirstHashElementIndentation" 55 | "Layout/FirstHashElementLineBreak" 56 | "Layout/FirstMethodArgumentLineBreak" 57 | "Layout/FirstMethodParameterLineBreak" 58 | "Layout/FirstParameterIndentation" 59 | "Layout/HashAlignment" 60 | "Layout/HeredocArgumentClosingParenthesis" 61 | "Layout/HeredocIndentation" 62 | "Layout/IndentationConsistency" 63 | "Layout/IndentationStyle" 64 | "Layout/IndentationWidth" 65 | "Layout/InitialIndentation" 66 | "Layout/LeadingCommentSpace" 67 | "Layout/LeadingEmptyLines" 68 | "Layout/LineContinuationLeadingSpace" 69 | "Layout/LineContinuationSpacing" 70 | "Layout/LineEndStringConcatenationIndentation" 71 | "Layout/MultilineArrayBraceLayout" 72 | "Layout/MultilineArrayLineBreaks" 73 | "Layout/MultilineAssignmentLayout" 74 | "Layout/MultilineBlockLayout" 75 | "Layout/MultilineHashBraceLayout" 76 | "Layout/MultilineHashKeyLineBreaks" 77 | "Layout/MultilineMethodArgumentLineBreaks" 78 | "Layout/MultilineMethodCallBraceLayout" 79 | "Layout/MultilineMethodCallIndentation" 80 | "Layout/MultilineMethodDefinitionBraceLayout" 81 | "Layout/MultilineMethodParameterLineBreaks" 82 | "Layout/MultilineOperationIndentation" 83 | "Layout/ParameterAlignment" 84 | "Layout/RedundantLineBreak" 85 | "Layout/RescueEnsureAlignment" 86 | "Layout/SingleLineBlockChain" 87 | "Layout/SpaceAfterColon" 88 | "Layout/SpaceAfterComma" 89 | "Layout/SpaceAfterMethodName" 90 | "Layout/SpaceAfterNot" 91 | "Layout/SpaceAfterSemicolon" 92 | "Layout/SpaceAroundBlockParameters" 93 | "Layout/SpaceAroundEqualsInParameterDefault" 94 | "Layout/SpaceAroundKeyword" 95 | "Layout/SpaceAroundMethodCallOperator" 96 | "Layout/SpaceAroundOperators" 97 | "Layout/SpaceBeforeBlockBraces" 98 | "Layout/SpaceBeforeBrackets" 99 | "Layout/SpaceBeforeComma" 100 | "Layout/SpaceBeforeComment" 101 | "Layout/SpaceBeforeFirstArg" 102 | "Layout/SpaceBeforeSemicolon" 103 | "Layout/SpaceInLambdaLiteral" 104 | "Layout/SpaceInsideArrayLiteralBrackets" 105 | "Layout/SpaceInsideArrayPercentLiteral" 106 | "Layout/SpaceInsideBlockBraces" 107 | "Layout/SpaceInsideHashLiteralBraces" 108 | "Layout/SpaceInsideParens" 109 | "Layout/SpaceInsidePercentLiteralDelimiters" 110 | "Layout/SpaceInsideRangeLiteral" 111 | "Layout/SpaceInsideReferenceBrackets" 112 | "Layout/SpaceInsideStringInterpolation" 113 | "Layout/TrailingEmptyLines" 114 | "Layout/TrailingWhitespace" 115 | "Lint/AmbiguousAssignment" 116 | "Lint/AmbiguousBlockAssociation" 117 | "Lint/AmbiguousOperator" 118 | "Lint/AmbiguousOperatorPrecedence" 119 | "Lint/AmbiguousRange" 120 | "Lint/AmbiguousRegexpLiteral" 121 | "Lint/AssignmentInCondition" 122 | "Lint/BigDecimalNew" 123 | "Lint/BinaryOperatorWithIdenticalOperands" 124 | "Lint/BooleanSymbol" 125 | "Lint/CircularArgumentReference" 126 | "Lint/ConstantDefinitionInBlock" 127 | "Lint/ConstantOverwrittenInRescue" 128 | "Lint/ConstantResolution" 129 | "Lint/Debugger" 130 | "Lint/DeprecatedClassMethods" 131 | "Lint/DeprecatedConstants" 132 | "Lint/DeprecatedOpenSSLConstant" 133 | "Lint/DisjunctiveAssignmentInConstructor" 134 | "Lint/DuplicateBranch" 135 | "Lint/DuplicateCaseCondition" 136 | "Lint/DuplicateElsifCondition" 137 | "Lint/DuplicateHashKey" 138 | "Lint/DuplicateMagicComment" 139 | "Lint/DuplicateMatchPattern" 140 | "Lint/DuplicateMethods" 141 | "Lint/DuplicateRegexpCharacterClassElement" 142 | "Lint/DuplicateRequire" 143 | "Lint/DuplicateRescueException" 144 | "Lint/EachWithObjectArgument" 145 | "Lint/ElseLayout" 146 | "Lint/EmptyBlock" 147 | "Lint/EmptyClass" 148 | "Lint/EmptyConditionalBody" 149 | "Lint/EmptyEnsure" 150 | "Lint/EmptyExpression" 151 | "Lint/EmptyFile" 152 | "Lint/EmptyInPattern" 153 | "Lint/EmptyInterpolation" 154 | "Lint/EmptyWhen" 155 | "Lint/EnsureReturn" 156 | "Lint/ErbNewArguments" 157 | "Lint/FlipFlop" 158 | "Lint/FloatComparison" 159 | "Lint/FloatOutOfRange" 160 | "Lint/FormatParameterMismatch" 161 | "Lint/HashCompareByIdentity" 162 | "Lint/HeredocMethodCallPosition" 163 | "Lint/IdentityComparison" 164 | "Lint/ImplicitStringConcatenation" 165 | "Lint/IncompatibleIoSelectWithFiberScheduler" 166 | "Lint/IneffectiveAccessModifier" 167 | "Lint/InheritException" 168 | "Lint/InterpolationCheck" 169 | "Lint/LambdaWithoutLiteralBlock" 170 | "Lint/LiteralAsCondition" 171 | "Lint/LiteralInInterpolation" 172 | "Lint/Loop" 173 | "Lint/MissingCopEnableDirective" 174 | "Lint/MissingSuper" 175 | "Lint/MixedRegexpCaptureTypes" 176 | "Lint/MultipleComparison" 177 | "Lint/NestedMethodDefinition" 178 | "Lint/NestedPercentLiteral" 179 | "Lint/NextWithoutAccumulator" 180 | "Lint/NoReturnInBeginEndBlocks" 181 | "Lint/NonAtomicFileOperation" 182 | "Lint/NonDeterministicRequireOrder" 183 | "Lint/NonLocalExitFromIterator" 184 | "Lint/NumberConversion" 185 | "Lint/NumberedParameterAssignment" 186 | "Lint/OrAssignmentToConstant" 187 | "Lint/OrderedMagicComments" 188 | "Lint/OutOfRangeRegexpRef" 189 | "Lint/ParenthesesAsGroupedExpression" 190 | "Lint/PercentStringArray" 191 | "Lint/PercentSymbolArray" 192 | "Lint/RaiseException" 193 | "Lint/RandOne" 194 | "Lint/RedundantCopDisableDirective" 195 | "Lint/RedundantCopEnableDirective" 196 | "Lint/RedundantDirGlobSort" 197 | "Lint/RedundantRequireStatement" 198 | "Lint/RedundantSafeNavigation" 199 | "Lint/RedundantSplatExpansion" 200 | "Lint/RedundantStringCoercion" 201 | "Lint/RedundantWithIndex" 202 | "Lint/RedundantWithObject" 203 | "Lint/RefinementImportMethods" 204 | "Lint/RegexpAsCondition" 205 | "Lint/RequireParentheses" 206 | "Lint/RequireRangeParentheses" 207 | "Lint/RequireRelativeSelfPath" 208 | "Lint/RescueException" 209 | "Lint/RescueType" 210 | "Lint/ReturnInVoidContext" 211 | "Lint/SafeNavigationChain" 212 | "Lint/SafeNavigationConsistency" 213 | "Lint/SafeNavigationWithEmpty" 214 | "Lint/ScriptPermission" 215 | "Lint/SelfAssignment" 216 | "Lint/SendWithMixinArgument" 217 | "Lint/ShadowedArgument" 218 | "Lint/ShadowedException" 219 | "Lint/ShadowingOuterLocalVariable" 220 | "Lint/StructNewOverride" 221 | "Lint/SuppressedException" 222 | "Lint/SymbolConversion" 223 | "Lint/Syntax" 224 | "Lint/ToEnumArguments" 225 | "Lint/ToJSON" 226 | "Lint/TopLevelReturnWithArgument" 227 | "Lint/TrailingCommaInAttributeDeclaration" 228 | "Lint/TripleQuotes" 229 | "Lint/UnderscorePrefixedVariableName" 230 | "Lint/UnexpectedBlockArity" 231 | "Lint/UnifiedInteger" 232 | "Lint/UnmodifiedReduceAccumulator" 233 | "Lint/UnreachableCode" 234 | "Lint/UnreachableLoop" 235 | "Lint/UnusedBlockArgument" 236 | "Lint/UnusedMethodArgument" 237 | "Lint/UriEscapeUnescape" 238 | "Lint/UriRegexp" 239 | "Lint/UselessAccessModifier" 240 | "Lint/UselessAssignment" 241 | "Lint/UselessElseWithoutRescue" 242 | "Lint/UselessMethodDefinition" 243 | "Lint/UselessRescue" 244 | "Lint/UselessRuby2Keywords" 245 | "Lint/UselessSetterCall" 246 | "Lint/UselessTimes" 247 | "Lint/Void" 248 | "Metrics/AbcSize" 249 | "Metrics/BlockLength" 250 | "Metrics/BlockNesting" 251 | "Metrics/ClassLength" 252 | "Metrics/CollectionLiteralLength" 253 | "Metrics/CyclomaticComplexity" 254 | "Metrics/LineLength" 255 | "Metrics/MethodLength" 256 | "Metrics/ModuleLength" 257 | "Metrics/ParameterLists" 258 | "Metrics/PerceivedComplexity" 259 | "Migration/DepartmentName" 260 | "Naming/AccessorMethodName" 261 | "Naming/AsciiIdentifiers" 262 | "Naming/BinaryOperatorParameterName" 263 | "Naming/BlockForwarding" 264 | "Naming/BlockParameterName" 265 | "Naming/ClassAndModuleCamelCase" 266 | "Naming/ConstantName" 267 | "Naming/FileName" 268 | "Naming/HeredocDelimiterCase" 269 | "Naming/HeredocDelimiterNaming" 270 | "Naming/InclusiveLanguage" 271 | "Naming/MemoizedInstanceVariableName" 272 | "Naming/MethodName" 273 | "Naming/MethodParameterName" 274 | "Naming/PredicateName" 275 | "Naming/RescuedExceptionsVariableName" 276 | "Naming/VariableName" 277 | "Naming/VariableNumber" 278 | "Performance/AncestorsInclude" 279 | "Performance/ArraySemiInfiniteRangeSlice" 280 | "Performance/BigDecimalWithNumericArgument" 281 | "Performance/BindCall" 282 | "Performance/BlockGivenWithExplicitBlock" 283 | "Performance/Caller" 284 | "Performance/CaseWhenSplat" 285 | "Performance/Casecmp" 286 | "Performance/ChainArrayAllocation" 287 | "Performance/CollectionLiteralInLoop" 288 | "Performance/CompareWithBlock" 289 | "Performance/ConcurrentMonotonicTime" 290 | "Performance/ConstantRegexp" 291 | "Performance/Count" 292 | "Performance/DeletePrefix" 293 | "Performance/DeleteSuffix" 294 | "Performance/Detect" 295 | "Performance/DoubleStartEndWith" 296 | "Performance/EndWith" 297 | "Performance/FixedSize" 298 | "Performance/FlatMap" 299 | "Performance/InefficientHashSearch" 300 | "Performance/IoReadlines" 301 | "Performance/MapCompact" 302 | "Performance/MethodObjectAsBlock" 303 | "Performance/OpenStruct" 304 | "Performance/RangeInclude" 305 | "Performance/RedundantBlockCall" 306 | "Performance/RedundantEqualityComparisonBlock" 307 | "Performance/RedundantMatch" 308 | "Performance/RedundantMerge" 309 | "Performance/RedundantSortBlock" 310 | "Performance/RedundantSplitRegexpArgument" 311 | "Performance/RedundantStringChars" 312 | "Performance/RegexpMatch" 313 | "Performance/ReverseEach" 314 | "Performance/ReverseFirst" 315 | "Performance/SelectMap" 316 | "Performance/Size" 317 | "Performance/SortReverse" 318 | "Performance/Squeeze" 319 | "Performance/StartWith" 320 | "Performance/StringIdentifierArgument" 321 | "Performance/StringInclude" 322 | "Performance/StringReplacement" 323 | "Performance/Sum" 324 | "Performance/TimesMap" 325 | "Performance/UnfreezeString" 326 | "Performance/UriDefaultParser" 327 | "RSpec/AlignLeftLetBrace" 328 | "RSpec/AlignRightLetBrace" 329 | "RSpec/AnyInstance" 330 | "RSpec/AroundBlock" 331 | "RSpec/Be" 332 | "RSpec/BeEq" 333 | "RSpec/BeEql" 334 | "RSpec/BeNil" 335 | "RSpec/BeforeAfterAll" 336 | "RSpec/ChangeByZero" 337 | "RSpec/ContextMethod" 338 | "RSpec/ContextWording" 339 | "RSpec/DescribeClass" 340 | "RSpec/DescribeMethod" 341 | "RSpec/DescribeSymbol" 342 | "RSpec/DescribedClass" 343 | "RSpec/DescribedClassModuleWrapping" 344 | "RSpec/Dialect" 345 | "RSpec/EmptyExampleGroup" 346 | "RSpec/EmptyHook" 347 | "RSpec/EmptyLineAfterExample" 348 | "RSpec/EmptyLineAfterExampleGroup" 349 | "RSpec/EmptyLineAfterFinalLet" 350 | "RSpec/EmptyLineAfterHook" 351 | "RSpec/EmptyLineAfterSubject" 352 | "RSpec/ExampleLength" 353 | "RSpec/ExampleWithoutDescription" 354 | "RSpec/ExampleWording" 355 | "RSpec/ExcessiveDocstringSpacing" 356 | "RSpec/ExpectActual" 357 | "RSpec/ExpectChange" 358 | "RSpec/ExpectInHook" 359 | "RSpec/ExpectOutput" 360 | "RSpec/FilePath" 361 | "RSpec/Focus" 362 | "RSpec/HookArgument" 363 | "RSpec/HooksBeforeExamples" 364 | "RSpec/IdenticalEqualityAssertion" 365 | "RSpec/ImplicitBlockExpectation" 366 | "RSpec/ImplicitExpect" 367 | "RSpec/ImplicitSubject" 368 | "RSpec/InstanceSpy" 369 | "RSpec/InstanceVariable" 370 | "RSpec/ItBehavesLike" 371 | "RSpec/IteratedExpectation" 372 | "RSpec/LeadingSubject" 373 | "RSpec/LeakyConstantDeclaration" 374 | "RSpec/LetBeforeExamples" 375 | "RSpec/LetSetup" 376 | "RSpec/MessageChain" 377 | "RSpec/MessageExpectation" 378 | "RSpec/MessageSpies" 379 | "RSpec/MissingExampleGroupArgument" 380 | "RSpec/MultipleDescribes" 381 | "RSpec/MultipleExpectations" 382 | "RSpec/MultipleMemoizedHelpers" 383 | "RSpec/MultipleSubjects" 384 | "RSpec/NamedSubject" 385 | "RSpec/NestedGroups" 386 | "RSpec/NotToNot" 387 | "RSpec/OverwritingSetup" 388 | "RSpec/Pending" 389 | "RSpec/PredicateMatcher" 390 | "RSpec/ReceiveCounts" 391 | "RSpec/ReceiveNever" 392 | "RSpec/RepeatedDescription" 393 | "RSpec/RepeatedExample" 394 | "RSpec/RepeatedExampleGroupBody" 395 | "RSpec/RepeatedExampleGroupDescription" 396 | "RSpec/RepeatedIncludeExample" 397 | "RSpec/ReturnFromStub" 398 | "RSpec/ScatteredLet" 399 | "RSpec/ScatteredSetup" 400 | "RSpec/SharedContext" 401 | "RSpec/SharedExamples" 402 | "RSpec/SingleArgumentMessageChain" 403 | "RSpec/StubbedMock" 404 | "RSpec/SubjectDeclaration" 405 | "RSpec/SubjectStub" 406 | "RSpec/UnspecifiedException" 407 | "RSpec/VariableDefinition" 408 | "RSpec/VariableName" 409 | "RSpec/VerifiedDoubleReference" 410 | "RSpec/VerifiedDoubles" 411 | "RSpec/VoidExpect" 412 | "RSpec/Yield" 413 | "RSpec/Capybara/CurrentPathExpectation" 414 | "RSpec/Capybara/FeatureMethods" 415 | "RSpec/Capybara/VisibilityMatcher" 416 | "RSpec/FactoryBot/AttributeDefinedStatically" 417 | "RSpec/FactoryBot/CreateList" 418 | "RSpec/FactoryBot/FactoryClassName" 419 | "RSpec/FactoryBot/SyntaxMethods" 420 | "RSpec/Rails/AvoidSetupHook" 421 | "RSpec/Rails/HttpStatus" 422 | "Rails/ActionControllerTestCase" 423 | "Rails/ActionFilter" 424 | "Rails/ActiveRecordAliases" 425 | "Rails/ActiveRecordCallbacksOrder" 426 | "Rails/ActiveRecordOverride" 427 | "Rails/ActiveSupportAliases" 428 | "Rails/AddColumnIndex" 429 | "Rails/AfterCommitOverride" 430 | "Rails/ApplicationController" 431 | "Rails/ApplicationJob" 432 | "Rails/ApplicationMailer" 433 | "Rails/ApplicationRecord" 434 | "Rails/ArelStar" 435 | "Rails/AssertNot" 436 | "Rails/AttributeDefaultBlockValue" 437 | "Rails/BelongsTo" 438 | "Rails/Blank" 439 | "Rails/BulkChangeTable" 440 | "Rails/CompactBlank" 441 | "Rails/ContentTag" 442 | "Rails/CreateTableWithTimestamps" 443 | "Rails/Date" 444 | "Rails/DefaultScope" 445 | "Rails/Delegate" 446 | "Rails/DelegateAllowBlank" 447 | "Rails/DeprecatedActiveModelErrorsMethods" 448 | "Rails/DotSeparatedKeys" 449 | "Rails/DuplicateAssociation" 450 | "Rails/DuplicateScope" 451 | "Rails/DurationArithmetic" 452 | "Rails/DynamicFindBy" 453 | "Rails/EagerEvaluationLogMessage" 454 | "Rails/EnumHash" 455 | "Rails/EnumUniqueness" 456 | "Rails/EnvironmentComparison" 457 | "Rails/EnvironmentVariableAccess" 458 | "Rails/Exit" 459 | "Rails/ExpandedDateRange" 460 | "Rails/FilePath" 461 | "Rails/FindBy" 462 | "Rails/FindById" 463 | "Rails/FindEach" 464 | "Rails/HasAndBelongsToMany" 465 | "Rails/HasManyOrHasOneDependent" 466 | "Rails/HelperInstanceVariable" 467 | "Rails/HttpPositionalArguments" 468 | "Rails/HttpStatus" 469 | "Rails/I18nLazyLookup" 470 | "Rails/I18nLocaleAssignment" 471 | "Rails/I18nLocaleTexts" 472 | "Rails/IgnoredSkipActionFilterOption" 473 | "Rails/IndexBy" 474 | "Rails/IndexWith" 475 | "Rails/Inquiry" 476 | "Rails/InverseOf" 477 | "Rails/LexicallyScopedActionFilter" 478 | "Rails/LinkToBlank" 479 | "Rails/MailerName" 480 | "Rails/MatchRoute" 481 | "Rails/MigrationClassName" 482 | "Rails/NegateInclude" 483 | "Rails/NotNullColumn" 484 | "Rails/OrderById" 485 | "Rails/Output" 486 | "Rails/OutputSafety" 487 | "Rails/Pick" 488 | "Rails/Pluck" 489 | "Rails/PluckId" 490 | "Rails/PluckInWhere" 491 | "Rails/PluralizationGrammar" 492 | "Rails/Presence" 493 | "Rails/Present" 494 | "Rails/RakeEnvironment" 495 | "Rails/ReadWriteAttribute" 496 | "Rails/RedundantAllowNil" 497 | "Rails/RedundantForeignKey" 498 | "Rails/RedundantPresenceValidationOnBelongsTo" 499 | "Rails/RedundantReceiverInWithOptions" 500 | "Rails/RedundantTravelBack" 501 | "Rails/ReflectionClassName" 502 | "Rails/RefuteMethods" 503 | "Rails/RelativeDateConstant" 504 | "Rails/RenderInline" 505 | "Rails/RenderPlainText" 506 | "Rails/RequestReferer" 507 | "Rails/RequireDependency" 508 | "Rails/ReversibleMigration" 509 | "Rails/ReversibleMigrationMethodDefinition" 510 | "Rails/RootJoinChain" 511 | "Rails/RootPublicPath" 512 | "Rails/SafeNavigation" 513 | "Rails/SafeNavigationWithBlank" 514 | "Rails/SaveBang" 515 | "Rails/SchemaComment" 516 | "Rails/ScopeArgs" 517 | "Rails/ShortI18n" 518 | "Rails/SkipsModelValidations" 519 | "Rails/SquishedSQLHeredocs" 520 | "Rails/StripHeredoc" 521 | "Rails/TableNameAssignment" 522 | "Rails/TimeZone" 523 | "Rails/TimeZoneAssignment" 524 | "Rails/ToFormattedS" 525 | "Rails/TransactionExitStatement" 526 | "Rails/UniqBeforePluck" 527 | "Rails/UniqueValidationWithoutIndex" 528 | "Rails/UnknownEnv" 529 | "Rails/UnusedIgnoredColumns" 530 | "Rails/Validation" 531 | "Rails/WhereEquals" 532 | "Rails/WhereExists" 533 | "Rails/WhereNot" 534 | "Security/CompoundHash" 535 | "Security/Eval" 536 | "Security/IoMethods" 537 | "Security/JSONLoad" 538 | "Security/MarshalLoad" 539 | "Security/Open" 540 | "Security/YAMLLoad" 541 | "Style/AccessModifierDeclarations" 542 | "Style/AccessorGrouping" 543 | "Style/Alias" 544 | "Style/AndOr" 545 | "Style/ArgumentsForwarding" 546 | "Style/ArrayCoercion" 547 | "Style/ArrayIntersect" 548 | "Style/ArrayJoin" 549 | "Style/AsciiComments" 550 | "Style/Attr" 551 | "Style/AutoResourceCleanup" 552 | "Style/BarePercentLiterals" 553 | "Style/BeginBlock" 554 | "Style/BisectedAttrAccessor" 555 | "Style/BlockComments" 556 | "Style/BlockDelimiters" 557 | "Style/CaseEquality" 558 | "Style/CaseLikeIf" 559 | "Style/CharacterLiteral" 560 | "Style/ClassAndModuleChildren" 561 | "Style/ClassCheck" 562 | "Style/ClassEqualityComparison" 563 | "Style/ClassMethods" 564 | "Style/ClassMethodsDefinitions" 565 | "Style/ClassVars" 566 | "Style/CollectionCompact" 567 | "Style/CollectionMethods" 568 | "Style/ColonMethodCall" 569 | "Style/ColonMethodDefinition" 570 | "Style/CombinableLoops" 571 | "Style/CommandLiteral" 572 | "Style/CommentAnnotation" 573 | "Style/CommentedKeyword" 574 | "Style/ComparableClamp" 575 | "Style/ConcatArrayLiterals" 576 | "Style/ConditionalAssignment" 577 | "Style/ConstantVisibility" 578 | "Style/Copyright" 579 | "Style/DataInheritance" 580 | "Style/DateTime" 581 | "Style/DefWithParentheses" 582 | "Style/Dir" 583 | "Style/DirEmpty" 584 | "Style/DisableCopsWithinSourceCodeDirective" 585 | "Style/DocumentDynamicEvalDefinition" 586 | "Style/Documentation" 587 | "Style/DocumentationMethod" 588 | "Style/DoubleCopDisableDirective" 589 | "Style/DoubleNegation" 590 | "Style/EachForSimpleLoop" 591 | "Style/EachWithObject" 592 | "Style/EmptyBlockParameter" 593 | "Style/EmptyCaseCondition" 594 | "Style/EmptyElse" 595 | "Style/EmptyHeredoc" 596 | "Style/EmptyLambdaParameter" 597 | "Style/EmptyLiteral" 598 | "Style/EmptyMethod" 599 | "Style/Encoding" 600 | "Style/EndBlock" 601 | "Style/EndlessMethod" 602 | "Style/EnvHome" 603 | "Style/EvalWithLocation" 604 | "Style/EvenOdd" 605 | "Style/ExpandPathArguments" 606 | "Style/ExplicitBlockArgument" 607 | "Style/ExponentialNotation" 608 | "Style/FetchEnvVar" 609 | "Style/FileEmpty" 610 | "Style/FileRead" 611 | "Style/FileWrite" 612 | "Style/FloatDivision" 613 | "Style/For" 614 | "Style/FormatString" 615 | "Style/FormatStringToken" 616 | "Style/FrozenStringLiteralComment" 617 | "Style/GlobalStdStream" 618 | "Style/GlobalVars" 619 | "Style/GuardClause" 620 | "Style/HashAsLastArrayItem" 621 | "Style/HashConversion" 622 | "Style/HashEachMethods" 623 | "Style/HashExcept" 624 | "Style/HashLikeCase" 625 | "Style/HashSyntax" 626 | "Style/HashTransformKeys" 627 | "Style/HashTransformValues" 628 | "Style/IdenticalConditionalBranches" 629 | "Style/IfInsideElse" 630 | "Style/IfUnlessModifier" 631 | "Style/IfUnlessModifierOfIfUnless" 632 | "Style/IfWithBooleanLiteralBranches" 633 | "Style/IfWithSemicolon" 634 | "Style/ImplicitRuntimeError" 635 | "Style/InPatternThen" 636 | "Style/InfiniteLoop" 637 | "Style/InlineComment" 638 | "Style/InverseMethods" 639 | "Style/InvertibleUnlessCondition" 640 | "Style/IpAddresses" 641 | "Style/KeywordParametersOrder" 642 | "Style/Lambda" 643 | "Style/LambdaCall" 644 | "Style/LineEndConcatenation" 645 | "Style/MagicCommentFormat" 646 | "Style/MapCompactWithConditionalBlock" 647 | "Style/MapToHash" 648 | "Style/MapToSet" 649 | "Style/MethodCallWithArgsParentheses" 650 | "Style/MethodCallWithoutArgsParentheses" 651 | "Style/MethodCalledOnDoEndBlock" 652 | "Style/MethodDefParentheses" 653 | "Style/MinMax" 654 | "Style/MinMaxComparison" 655 | "Style/MissingElse" 656 | "Style/MissingRespondToMissing" 657 | "Style/MixinGrouping" 658 | "Style/MixinUsage" 659 | "Style/ModuleFunction" 660 | "Style/MultilineBlockChain" 661 | "Style/MultilineIfModifier" 662 | "Style/MultilineIfThen" 663 | "Style/MultilineInPatternThen" 664 | "Style/MultilineMemoization" 665 | "Style/MultilineMethodSignature" 666 | "Style/MultilineTernaryOperator" 667 | "Style/MultilineWhenThen" 668 | "Style/MultipleComparison" 669 | "Style/MutableConstant" 670 | "Style/NegatedIf" 671 | "Style/NegatedIfElseCondition" 672 | "Style/NegatedUnless" 673 | "Style/NegatedWhile" 674 | "Style/NestedFileDirname" 675 | "Style/NestedModifier" 676 | "Style/NestedParenthesizedCalls" 677 | "Style/NestedTernaryOperator" 678 | "Style/Next" 679 | "Style/NilComparison" 680 | "Style/NilLambda" 681 | "Style/NonNilCheck" 682 | "Style/Not" 683 | "Style/NumberedParameters" 684 | "Style/NumberedParametersLimit" 685 | "Style/NumericLiteralPrefix" 686 | "Style/NumericLiterals" 687 | "Style/NumericPredicate" 688 | "Style/ObjectThen" 689 | "Style/OneLineConditional" 690 | "Style/OpenStructUse" 691 | "Style/OperatorMethodCall" 692 | "Style/OptionHash" 693 | "Style/OptionalArguments" 694 | "Style/OptionalBooleanParameter" 695 | "Style/OrAssignment" 696 | "Style/ParallelAssignment" 697 | "Style/ParenthesesAroundCondition" 698 | "Style/PercentLiteralDelimiters" 699 | "Style/PercentQLiterals" 700 | "Style/PerlBackrefs" 701 | "Style/PreferredHashMethods" 702 | "Style/Proc" 703 | "Style/QuotedSymbols" 704 | "Style/RaiseArgs" 705 | "Style/RandomWithOffset" 706 | "Style/RedundantArgument" 707 | "Style/RedundantAssignment" 708 | "Style/RedundantBegin" 709 | "Style/RedundantCapitalW" 710 | "Style/RedundantCondition" 711 | "Style/RedundantConditional" 712 | "Style/RedundantConstantBase" 713 | "Style/RedundantDoubleSplatHashBraces" 714 | "Style/RedundantEach" 715 | "Style/RedundantException" 716 | "Style/RedundantFetchBlock" 717 | "Style/RedundantFileExtensionInRequire" 718 | "Style/RedundantFreeze" 719 | "Style/RedundantHeredocDelimiterQuotes" 720 | "Style/RedundantInitialize" 721 | "Style/RedundantInterpolation" 722 | "Style/RedundantLineContinuation" 723 | "Style/RedundantParentheses" 724 | "Style/RedundantPercentQ" 725 | "Style/RedundantRegexpCharacterClass" 726 | "Style/RedundantRegexpEscape" 727 | "Style/RedundantReturn" 728 | "Style/RedundantSelf" 729 | "Style/RedundantSelfAssignment" 730 | "Style/RedundantSelfAssignmentBranch" 731 | "Style/RedundantSort" 732 | "Style/RedundantSortBy" 733 | "Style/RedundantStringEscape" 734 | "Style/RegexpLiteral" 735 | "Style/RequireOrder" 736 | "Style/RescueModifier" 737 | "Style/RescueStandardError" 738 | "Style/ReturnNil" 739 | "Style/SafeNavigation" 740 | "Style/Sample" 741 | "Style/SelectByRegexp" 742 | "Style/SelfAssignment" 743 | "Style/Semicolon" 744 | "Style/Send" 745 | "Style/SignalException" 746 | "Style/SingleArgumentDig" 747 | "Style/SingleLineBlockParams" 748 | "Style/SingleLineMethods" 749 | "Style/SlicingWithRange" 750 | "Style/SoleNestedConditional" 751 | "Style/SpecialGlobalVars" 752 | "Style/StabbyLambdaParentheses" 753 | "Style/StaticClass" 754 | "Style/StderrPuts" 755 | "Style/StringChars" 756 | "Style/StringConcatenation" 757 | "Style/StringHashKeys" 758 | "Style/StringLiterals" 759 | "Style/StringLiteralsInInterpolation" 760 | "Style/StringMethods" 761 | "Style/Strip" 762 | "Style/StructInheritance" 763 | "Style/SwapValues" 764 | "Style/SymbolArray" 765 | "Style/SymbolLiteral" 766 | "Style/SymbolProc" 767 | "Style/TernaryParentheses" 768 | "Style/TopLevelMethodDefinition" 769 | "Style/TrailingBodyOnClass" 770 | "Style/TrailingBodyOnMethodDefinition" 771 | "Style/TrailingBodyOnModule" 772 | "Style/TrailingCommaInArguments" 773 | "Style/TrailingCommaInArrayLiteral" 774 | "Style/TrailingCommaInBlockArgs" 775 | "Style/TrailingCommaInHashLiteral" 776 | "Style/TrailingMethodEndStatement" 777 | "Style/TrailingUnderscoreVariable" 778 | "Style/TrivialAccessors" 779 | "Style/UnlessElse" 780 | "Style/UnlessLogicalOperators" 781 | "Style/UnpackFirst" 782 | "Style/VariableInterpolation" 783 | "Style/WhenThen" 784 | "Style/WhileUntilDo" 785 | "Style/WhileUntilModifier" 786 | "Style/WordArray" 787 | "Style/YodaCondition" 788 | "Style/YodaExpression" 789 | "Style/ZeroLengthPredicate") 790 | "List of all cops.") 791 | -------------------------------------------------------------------------------- /emacs.d/org-charsheet.el: -------------------------------------------------------------------------------- 1 | ;;; org-charsheet --- An org-mode character sheet manager -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2024 Jeremy Friesen 4 | ;; Author: Jeremy Friesen 5 | 6 | ;; This file is NOT part of GNU Emacs. 7 | ;; 8 | ;;; Commentary: 9 | ;; 10 | ;; A package for managing character sheets via org-mode. This code 11 | ;; implements aspects of "clocks" and "tracks" developed in Shawn 12 | ;; Tomkin's Ironsworn rules system. 13 | ;; 14 | ;;; Code: 15 | 16 | (require 's) 17 | (require 'org) 18 | 19 | (defvar org-charsheet/clocks 20 | '("" "󰪞" "󰪟" "󰪠" "󰪡" "󰪢" "󰪣" "󰪤" "󰪥") 21 | "Useful progress tracking clocks in 1/8 intervals.") 22 | 23 | (defvar org-charsheet/ticks-by-rank 24 | '(("troublesome" . 12) 25 | ("dangerous" . 8) 26 | ("formidable" . 4) 27 | ("extreme" . 2) 28 | ("epic" . 1)) 29 | "An alist of track rank and the corresponding number of ticks.") 30 | 31 | (defun org-charsheet/insert-clock (position) 32 | "Prompt for a clock POSITION then insert." 33 | (interactive (list 34 | (completing-read 35 | "Clock face: " org-charsheet/clocks nil t))) 36 | (insert position)) 37 | 38 | (defun org-charsheet/insert-clock-track (capacity) 39 | "Create a track of CAPACITY number of clocks." 40 | (interactive "nCapacity: ") 41 | (let ((track 42 | (s-repeat capacity ""))) 43 | (insert 44 | (if current-prefix-arg 45 | (org-charsheet/clock-track-incremement 46 | :track track 47 | :ticks (read-number "Starting number of ticks: " 0)) 48 | track)))) 49 | 50 | (defun org-charsheet/increment-progress (&optional element) 51 | "Increment 'PROGRESS' property for `org-mode' ELEMENT." 52 | (interactive) 53 | (save-excursion 54 | (if-let* 55 | ((title 56 | (if element 57 | (org-element-property :raw-value element) 58 | (completing-read "Progress Tracks: " 59 | (org-charsheet/headlines-with-property 60 | :property "PROGRESS") 61 | nil t))) 62 | (element 63 | (or element 64 | (car 65 | (org-charsheet/headlines-with-property 66 | :property "PROGRESS" 67 | :title title)))) 68 | (new-rank 69 | (org-charsheet/increment-progress-for-element element))) 70 | (progn 71 | (message "Updating %s to %s" title new-rank) 72 | (org-entry-put element "PROGRESS" new-rank)) 73 | (message "Headling %s; New Rank: %s" title new-rank)))) 74 | 75 | (cl-defun org-charsheet/headlines-with-property (&key property title) 76 | "Find all headlines with PROPERTY. 77 | 78 | When you provide a TITLE limit the headlines to those titles that match. 79 | 80 | Hopefully only one of them." 81 | (with-current-buffer (current-buffer) 82 | (org-element-map 83 | (org-element-parse-buffer) 84 | '(keyword node-property headline) 85 | (lambda (element) 86 | (and 87 | (org-element-type-p element 'headline) 88 | (not (s-blank? (org-entry-get element property))) 89 | (if title 90 | (string= (org-element-property :raw-value element) title) 91 | t) 92 | (org-element-property :title element)))))) 93 | 94 | (defun org-charsheet/increment-progress-for-element (element) 95 | "Given the ELEMENT adjust the 'PROGRESS' property. 96 | 97 | Using the 'PROGRESS' and 'RANK' to calculate the incrementation." 98 | (if-let* ((progress 99 | (org-entry-get element "PROGRESS")) 100 | (rank 101 | (downcase (org-entry-get element "RANK"))) 102 | (ticks 103 | (alist-get 104 | (downcase rank) 105 | org-charsheet/ticks-by-rank nil nil #'string=))) 106 | (org-charsheet/clock-track-incremement track ticks) 107 | (user-error "Element: %S; Progress: %S; Rank: %S" 108 | (org-element-property :raw-value element) 109 | progress rank))) 110 | 111 | (defun org-charsheet/clock-track-incremement (track ticks) 112 | "Return a TRACK incremented by a number of TICKS." 113 | (let*((track 114 | ;; String empty spaces in the track 115 | (replace-regexp-in-string "[[:space:]]+" "" track)) 116 | (len 117 | (length track)) 118 | (capacity 119 | (* len 4)) 120 | (initial-value 121 | (cl-reduce 122 | (lambda (acc clock) 123 | (+ 124 | acc 125 | (cond 126 | ((string= "" clock) 0) 127 | ((string= "󰪟" clock) 1) 128 | ((string= "󰪡" clock) 2) 129 | ((string= "󰪣" clock) 3) 130 | ((string= "󰪥" clock) 4) 131 | (t 132 | (user-error 133 | "Expected %s to be a quarter-increment clock" 134 | track))))) 135 | (split-string track "" t) 136 | :initial-value 0)) 137 | (updated-value 138 | (+ initial-value ticks)) 139 | (remainder (mod updated-value 4)) 140 | (filled-clocks (/ updated-value 4))) 141 | (concat 142 | (s-repeat filled-clocks "󰪥") 143 | (cond 144 | ((= 0 remainder) "") 145 | ((= 1 remainder) "󰪟") 146 | ((= 2 remainder) "󰪡") 147 | ((= 3 remainder) "󰪣") 148 | ((= 4 remainder) "󰪥")) 149 | (s-repeat (- len filled-clocks 1) "")))) 150 | 151 | 152 | (provide 'org-charsheet) 153 | ;;; org-charsheet.el ends here 154 | -------------------------------------------------------------------------------- /emacs.d/ox-hugo-simple.el: -------------------------------------------------------------------------------- 1 | ;;; ox-hugo-simple --- A Simplified Hugo Export -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2024 Jeremy Friesen 4 | ;; Author: Jeremy Friesen 5 | 6 | ;; This file is NOT part of GNU Emacs. 7 | ;;; Commentary: 8 | 9 | ;; A less configurable, more opinionated `org-mode' to Hugo exporter. 10 | ;;; Code: 11 | 12 | ;;; Variables 13 | (defvar ox-hugo-simple-footnotes-as-sidenotes t 14 | "Default `ox-md' exports footnotes as links at the bottom of 15 | the page. The author prefers sidenotes.") 16 | 17 | (defcustom ox-hugo-content-block-export-alist 18 | '((marginnote . ox-hugo-simple-content-block-marginnote) 19 | ; Baseline block elements. There are more 20 | (details . ox-hugo-simple-content-block-as-html-block-element) 21 | (summary . ox-hugo-simple-content-block-as-html-block-element) 22 | (aside . ox-hugo-simple-content-block-as-html-block-element) 23 | (header . ox-hugo-simple-content-block-as-html-block-element) 24 | (footer . ox-hugo-simple-content-block-as-html-block-element) 25 | (nav . ox-hugo-simple-content-block-as-html-block-element) 26 | (div . ox-hugo-simple-content-block-as-html-block-element)) 27 | "An alist of named content blocks (e.g. \"#+begin_details\") 28 | and their associated rendering function.") 29 | 30 | (provide 'ox-hugo-simple) 31 | ;;; ox-hugo-simple.el ends here 32 | -------------------------------------------------------------------------------- /emacs.d/prot-common.el: -------------------------------------------------------------------------------- 1 | ;;; prot-common.el --- Common functions for my dotemacs -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2020-2023 Protesilaos Stavrou 4 | 5 | ;; Author: Protesilaos Stavrou 6 | ;; URL: https://protesilaos.com/emacs/dotemacs 7 | ;; Version: 0.1.0 8 | ;; Package-Requires: ((emacs "30.1")) 9 | 10 | ;; This file is NOT part of GNU Emacs. 11 | 12 | ;; This program is free software; you can redistribute it and/or modify 13 | ;; it under the terms of the GNU General Public License as published by 14 | ;; the Free Software Foundation, either version 3 of the License, or (at 15 | ;; your option) any later version. 16 | ;; 17 | ;; This program is distributed in the hope that it will be useful, 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | ;; GNU General Public License for more details. 21 | ;; 22 | ;; You should have received a copy of the GNU General Public License 23 | ;; along with this program. If not, see . 24 | 25 | ;;; Commentary: 26 | ;; 27 | ;; Common functions for my Emacs: . 28 | ;; 29 | ;; Remember that every piece of Elisp that I write is for my own 30 | ;; educational and recreational purposes. I am not a programmer and I 31 | ;; do not recommend that you copy any of this if you are not certain of 32 | ;; what it does. 33 | 34 | ;;; Code: 35 | 36 | (eval-when-compile 37 | (require 'subr-x) 38 | (require 'cl-lib)) 39 | 40 | (defgroup prot-common () 41 | "Auxiliary functions for my dotemacs." 42 | :group 'editing) 43 | 44 | ;;;###autoload 45 | (defun prot-common-number-even-p (n) 46 | "Test if N is an even number." 47 | (if (numberp n) 48 | (= (% n 2) 0) 49 | (error "%s is not a number" n))) 50 | 51 | ;;;###autoload 52 | (defun prot-common-number-integer-p (n) 53 | "Test if N is an integer." 54 | (if (integerp n) 55 | n 56 | (error "%s is not an integer" n))) 57 | 58 | ;;;###autoload 59 | (defun prot-common-number-integer-positive-p (n) 60 | "Test if N is a positive integer." 61 | (if (prot-common-number-integer-p n) 62 | (> n 0) 63 | (error "%s is not a positive integer" n))) 64 | 65 | ;; Thanks to Gabriel for providing a cleaner version of 66 | ;; `prot-common-number-negative': . 67 | ;;;###autoload 68 | (defun prot-common-number-negative (n) 69 | "Make N negative." 70 | (if (and (numberp n) (> n 0)) 71 | (* -1 n) 72 | (error "%s is not a valid positive number" n))) 73 | 74 | ;;;###autoload 75 | (defun prot-common-reverse-percentage (number percent change-p) 76 | "Determine the original value of NUMBER given PERCENT. 77 | 78 | CHANGE-P should specify the increase or decrease. For simplicity, 79 | nil means decrease while non-nil stands for an increase. 80 | 81 | NUMBER must satisfy `numberp', while PERCENT must be `natnump'." 82 | (unless (numberp number) 83 | (user-error "NUMBER must satisfy numberp")) 84 | (unless (natnump percent) 85 | (user-error "PERCENT must satisfy natnump")) 86 | (let* ((pc (/ (float percent) 100)) 87 | (pc-change (if change-p (+ 1 pc) pc)) 88 | (n (if change-p pc-change (float (- 1 pc-change))))) 89 | ;; FIXME 2021-12-21: If float, round to 4 decimal points. 90 | (/ number n))) 91 | 92 | ;;;###autoload 93 | (defun prot-common-percentage-change (n-original n-final) 94 | "Find percentage change between N-ORIGINAL and N-FINAL numbers. 95 | 96 | When the percentage is not an integer, it is rounded to 4 97 | floating points: 16.666666666666664 => 16.667." 98 | (unless (numberp n-original) 99 | (user-error "N-ORIGINAL must satisfy numberp")) 100 | (unless (numberp n-final) 101 | (user-error "N-FINAL must satisfy numberp")) 102 | (let* ((difference (float (abs (- n-original n-final)))) 103 | (n (* (/ difference n-original) 100)) 104 | (round (floor n))) 105 | ;; FIXME 2021-12-21: Any way to avoid the `string-to-number'? 106 | (if (> n round) (string-to-number (format "%0.4f" n)) round))) 107 | 108 | ;; REVIEW 2023-04-07 07:43 +0300: I just wrote the conversions from 109 | ;; seconds. Hopefully they are correct, but I need to double check. 110 | (defun prot-common-seconds-to-minutes (seconds) 111 | "Convert a number representing SECONDS to MM:SS notation." 112 | (let ((minutes (/ seconds 60)) 113 | (seconds (% seconds 60))) 114 | (format "%.2d:%.2d" minutes seconds))) 115 | 116 | (defun prot-common-seconds-to-hours (seconds) 117 | "Convert a number representing SECONDS to HH:MM:SS notation." 118 | (let* ((hours (/ seconds 3600)) 119 | (minutes (/ (% seconds 3600) 60)) 120 | (seconds (% seconds 60))) 121 | (format "%.2d:%.2d:%.2d" hours minutes seconds))) 122 | 123 | ;;;###autoload 124 | (defun prot-common-seconds-to-minutes-or-hours (seconds) 125 | "Convert SECONDS to either minutes or hours, depending on the value." 126 | (if (> seconds 3599) 127 | (prot-common-seconds-to-hours seconds) 128 | (prot-common-seconds-to-minutes seconds))) 129 | 130 | ;;;###autoload 131 | (defun prot-common-rotate-list-of-symbol (symbol) 132 | "Rotate list value of SYMBOL by moving its car to the end. 133 | Return the first element before performing the rotation. 134 | 135 | This means that if `sample-list' has an initial value of `(one 136 | two three)', this function will first return `one' and update the 137 | value of `sample-list' to `(two three one)'. Subsequent calls 138 | will continue rotating accordingly." 139 | (unless (symbolp symbol) 140 | (user-error "%s is not a symbol" symbol)) 141 | (when-let* ((value (symbol-value symbol)) 142 | (list (and (listp value) value)) 143 | (first (car list))) 144 | (set symbol (append (cdr list) (list first))) 145 | first)) 146 | 147 | ;;;###autoload 148 | (defun prot-common-empty-buffer-p () 149 | "Test whether the buffer is empty." 150 | (or (= (point-min) (point-max)) 151 | (save-excursion 152 | (goto-char (point-min)) 153 | (while (and (looking-at "^\\([a-zA-Z]+: ?\\)?$") 154 | (zerop (forward-line 1)))) 155 | (eobp)))) 156 | 157 | ;;;###autoload 158 | (defun prot-common-minor-modes-active () 159 | "Return list of active minor modes for the current buffer." 160 | (let ((active-modes)) 161 | (mapc (lambda (m) 162 | (when (and (boundp m) (symbol-value m)) 163 | (push m active-modes))) 164 | minor-mode-list) 165 | active-modes)) 166 | 167 | ;;;###autoload 168 | (defun prot-common-truncate-lines-silently () 169 | "Toggle line truncation without printing messages." 170 | (let ((inhibit-message t)) 171 | (toggle-truncate-lines t))) 172 | 173 | ;; NOTE 2023-08-12: I tried the `clear-message-function', but it did 174 | ;; not work. What I need is very simple and this gets the job done. 175 | ;;;###autoload 176 | (defun prot-common-clear-minibuffer-message (&rest _) 177 | "Print an empty message to clear the echo area. 178 | Use this as advice :after a noisy function." 179 | (message "")) 180 | 181 | ;;;###autoload 182 | (defun prot-common-disable-hl-line () 183 | "Disable Hl-Line-Mode (for hooks)." 184 | (hl-line-mode -1)) 185 | 186 | ;;;###autoload 187 | (defun prot-common-window-bounds () 188 | "Return start and end points in the window as a cons cell." 189 | (cons (window-start) (window-end))) 190 | 191 | ;;;###autoload 192 | (defun prot-common-page-p () 193 | "Return non-nil if there is a `page-delimiter' in the buffer." 194 | (or (save-excursion (re-search-forward page-delimiter nil t)) 195 | (save-excursion (re-search-backward page-delimiter nil t)))) 196 | 197 | ;;;###autoload 198 | (defun prot-common-window-small-p () 199 | "Return non-nil if window is small. 200 | Check if the `window-width' or `window-height' is less than 201 | `split-width-threshold' and `split-height-threshold', 202 | respectively." 203 | (or (and (numberp split-width-threshold) 204 | (< (window-total-width) split-width-threshold)) 205 | (and (numberp split-height-threshold) 206 | (> (window-total-height) split-height-threshold)))) 207 | 208 | (defun prot-common-window-narrow-p () 209 | "Return non-nil if window is narrow. 210 | Check if the `window-width' is less than `split-width-threshold'." 211 | (and (numberp split-width-threshold) 212 | (< (window-total-width) split-width-threshold))) 213 | 214 | ;;;###autoload 215 | (defun prot-common-three-or-more-windows-p (&optional frame) 216 | "Return non-nil if three or more windows occupy FRAME. 217 | If FRAME is non-nil, inspect the current frame." 218 | (>= (length (window-list frame :no-minibuffer)) 3)) 219 | 220 | ;;;###autoload 221 | (defun prot-common-read-data (file) 222 | "Read Elisp data from FILE." 223 | (with-temp-buffer 224 | (insert-file-contents file) 225 | (read (current-buffer)))) 226 | 227 | ;;;###autoload 228 | (defun prot-common-completion-category () 229 | "Return completion category." 230 | (when-let ((window (active-minibuffer-window))) 231 | (with-current-buffer (window-buffer window) 232 | (completion-metadata-get 233 | (completion-metadata (buffer-substring-no-properties 234 | (minibuffer-prompt-end) 235 | (max (minibuffer-prompt-end) (point))) 236 | minibuffer-completion-table 237 | minibuffer-completion-predicate) 238 | 'category)))) 239 | 240 | ;; Thanks to Omar Antolín Camarena for providing this snippet! 241 | ;;;###autoload 242 | (defun prot-common-completion-table (category candidates) 243 | "Pass appropriate metadata CATEGORY to completion CANDIDATES. 244 | 245 | This is intended for bespoke functions that need to pass 246 | completion metadata that can then be parsed by other 247 | tools (e.g. `embark')." 248 | (lambda (string pred action) 249 | (if (eq action 'metadata) 250 | `(metadata (category . ,category)) 251 | (complete-with-action action candidates string pred)))) 252 | 253 | ;;;###autoload 254 | (defun prot-common-completion-table-no-sort (category candidates) 255 | "Pass appropriate metadata CATEGORY to completion CANDIDATES. 256 | Like `prot-common-completion-table' but also disable sorting." 257 | (lambda (string pred action) 258 | (if (eq action 'metadata) 259 | `(metadata (category . ,category) 260 | (display-sort-function . ,#'identity)) 261 | (complete-with-action action candidates string pred)))) 262 | 263 | ;; Thanks to Igor Lima for the `prot-common-crm-exclude-selected-p': 264 | ;; . 265 | ;; This is used as a filter predicate in the relevant prompts. 266 | (defvar crm-separator) 267 | 268 | ;;;###autoload 269 | (defun prot-common-crm-exclude-selected-p (input) 270 | "Filter out INPUT from `completing-read-multiple'. 271 | Hide non-destructively the selected entries from the completion 272 | table, thus avoiding the risk of inputting the same match twice. 273 | 274 | To be used as the PREDICATE of `completing-read-multiple'." 275 | (if-let* ((pos (string-match-p crm-separator input)) 276 | (rev-input (reverse input)) 277 | (element (reverse 278 | (substring rev-input 0 279 | (string-match-p crm-separator rev-input)))) 280 | (flag t)) 281 | (progn 282 | (while pos 283 | (if (string= (substring input 0 pos) element) 284 | (setq pos nil) 285 | (setq input (substring input (1+ pos)) 286 | pos (string-match-p crm-separator input) 287 | flag (when pos t)))) 288 | (not flag)) 289 | t)) 290 | 291 | ;; The `prot-common-line-regexp-p' and `prot-common--line-regexp-alist' 292 | ;; are contributed by Gabriel: . They 293 | ;; provide a more elegant approach to using a macro, as shown further 294 | ;; below. 295 | (defvar prot-common--line-regexp-alist 296 | '((empty . "[\s\t]*$") 297 | (indent . "^[\s\t]+") 298 | (non-empty . "^.+$") 299 | (list . "^\\([\s\t#*+]+\\|[0-9]+[^\s]?[).]+\\)") 300 | (heading . "^[=-]+")) 301 | "Alist of regexp types used by `prot-common-line-regexp-p'.") 302 | 303 | (defun prot-common-line-regexp-p (type &optional n) 304 | "Test for TYPE on line. 305 | TYPE is the car of a cons cell in 306 | `prot-common--line-regexp-alist'. It matches a regular 307 | expression. 308 | 309 | With optional N, search in the Nth line from point." 310 | (save-excursion 311 | (goto-char (line-beginning-position)) 312 | (and (not (bobp)) 313 | (or (beginning-of-line n) t) 314 | (save-match-data 315 | (looking-at 316 | (alist-get type prot-common--line-regexp-alist)))))) 317 | 318 | ;; The `prot-common-shell-command-with-exit-code-and-output' function is 319 | ;; courtesy of Harold Carr, who also sent a patch that improved 320 | ;; `prot-eww-download-html' (from the `prot-eww.el' library). 321 | ;; 322 | ;; More about Harold: . 323 | (defun prot-common-shell-command-with-exit-code-and-output (command &rest args) 324 | "Run COMMAND with ARGS. 325 | Return the exit code and output in a list." 326 | (with-temp-buffer 327 | (list (apply 'call-process command nil (current-buffer) nil args) 328 | (buffer-string)))) 329 | 330 | (defvar prot-common-url-regexp 331 | (concat 332 | "~?\\<\\([-a-zA-Z0-9+&@#/%?=~_|!:,.;]*\\)" 333 | "[.@]" 334 | "\\([-a-zA-Z0-9+&@#/%?=~_|!:,.;]+\\)\\>/?") 335 | "Regular expression to match (most?) URLs or email addresses.") 336 | 337 | (autoload 'auth-source-search "auth-source") 338 | 339 | ;;;###autoload 340 | (defun prot-common-auth-get-field (host prop) 341 | "Find PROP in `auth-sources' for HOST entry." 342 | (when-let ((source (auth-source-search :host host))) 343 | (if (eq prop :secret) 344 | (funcall (plist-get (car source) prop)) 345 | (plist-get (flatten-list source) prop)))) 346 | 347 | ;;;###autoload 348 | (defun prot-common-parse-file-as-list (file) 349 | "Return the contents of FILE as a list of strings. 350 | Strings are split at newline characters and are then trimmed for 351 | negative space. 352 | 353 | Use this function to provide a list of candidates for 354 | completion (per `completing-read')." 355 | (split-string 356 | (with-temp-buffer 357 | (insert-file-contents file) 358 | (buffer-substring-no-properties (point-min) (point-max))) 359 | "\n" :omit-nulls "[\s\f\t\n\r\v]+")) 360 | 361 | (defun prot-common-ignore (&rest _) 362 | "Use this as override advice to make a function do nothing." 363 | nil) 364 | 365 | ;; NOTE 2023-06-02: The `prot-common-wcag-formula' and 366 | ;; `prot-common-contrast' are taken verbatim from my `modus-themes' 367 | ;; and renamed to have the prefix `prot-common-' instead of 368 | ;; `modus-themes-'. This is all my code, of course, but I do it this 369 | ;; way to ensure that this file is self-contained in case someone 370 | ;; copies it. 371 | 372 | ;; This is the WCAG formula: . 373 | (defun prot-common-wcag-formula (hex) 374 | "Get WCAG value of color value HEX. 375 | The value is defined in hexadecimal RGB notation, such #123456." 376 | (cl-loop for k in '(0.2126 0.7152 0.0722) 377 | for x in (color-name-to-rgb hex) 378 | sum (* k (if (<= x 0.03928) 379 | (/ x 12.92) 380 | (expt (/ (+ x 0.055) 1.055) 2.4))))) 381 | 382 | ;;;###autoload 383 | (defun prot-common-contrast (c1 c2) 384 | "Measure WCAG contrast ratio between C1 and C2. 385 | C1 and C2 are color values written in hexadecimal RGB." 386 | (let ((ct (/ (+ (prot-common-wcag-formula c1) 0.05) 387 | (+ (prot-common-wcag-formula c2) 0.05)))) 388 | (max ct (/ ct)))) 389 | 390 | ;;;; EXPERIMENTAL macros (not meant to be used anywhere) 391 | 392 | ;; TODO 2023-09-30: Try the same with `cl-defmacro' and &key 393 | (defmacro prot-common-if (condition &rest consequences) 394 | "Separate the CONSEQUENCES of CONDITION semantically. 395 | Like `if', `when', `unless' but done by using `:then' and `:else' 396 | keywords. The forms under each keyword of `:then' and `:else' 397 | belong to the given subset of CONSEQUENCES. 398 | 399 | - The absence of `:else' means: (if CONDITION (progn CONSEQUENCES)). 400 | - The absence of `:then' means: (if CONDITION nil CONSEQUENCES). 401 | - Otherwise: (if CONDITION (progn then-CONSEQUENCES) else-CONSEQUENCES)." 402 | (declare (indent 1)) 403 | (let (then-consequences else-consequences last-kw) 404 | (dolist (elt consequences) 405 | (let ((is-keyword (keywordp elt))) 406 | (cond 407 | ((and (not is-keyword) (eq last-kw :then)) 408 | (push elt then-consequences)) 409 | ((and (not is-keyword) (eq last-kw :else)) 410 | (push elt else-consequences)) 411 | ((and is-keyword (eq elt :then)) 412 | (setq last-kw :then)) 413 | ((and is-keyword (eq elt :else)) 414 | (setq last-kw :else))))) 415 | `(if ,condition 416 | ,(if then-consequences 417 | `(progn ,@(nreverse then-consequences)) 418 | nil) 419 | ,@(nreverse else-consequences)))) 420 | 421 | (provide 'prot-common) 422 | ;;; prot-common.el ends here 423 | -------------------------------------------------------------------------------- /emacs.d/prot-window.el: -------------------------------------------------------------------------------- 1 | ;;; prot-window.el --- Display-buffer and window-related extensions for my dotemacs -*- lexical-binding: t -*- 2 | 3 | ;; Copyright (C) 2023 Protesilaos Stavrou 4 | 5 | ;; Author: Protesilaos Stavrou 6 | ;; URL: https://protesilaos.com/emacs/dotemacs 7 | ;; Version: 0.1.0 8 | ;; Package-Requires: ((emacs "30.1")) 9 | 10 | ;; This file is NOT part of GNU Emacs. 11 | 12 | ;; This program is free software; you can redistribute it and/or modify 13 | ;; it under the terms of the GNU General Public License as published by 14 | ;; the Free Software Foundation, either version 3 of the License, or 15 | ;; (at your option) any later version. 16 | ;; 17 | ;; This program is distributed in the hope that it will be useful, 18 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | ;; GNU General Public License for more details. 21 | ;; 22 | ;; You should have received a copy of the GNU General Public License 23 | ;; along with this program. If not, see . 24 | 25 | ;;; Commentary: 26 | ;; 27 | ;; This covers my window and display-buffer extensions, for use in my 28 | ;; Emacs setup: https://protesilaos.com/emacs/dotemacs. 29 | ;; 30 | ;; Remember that every piece of Elisp that I write is for my own 31 | ;; educational and recreational purposes. I am not a programmer and I 32 | ;; do not recommend that you copy any of this if you are not certain of 33 | ;; what it does. 34 | 35 | ;;; Code: 36 | 37 | (require 'prot-common) 38 | 39 | (defvar prot-window-window-sizes 40 | '( :max-height (lambda () (floor (frame-height) 3)) 41 | :min-height 10 42 | :max-width (lambda () (floor (frame-width) 4)) 43 | :min-width 20) 44 | "Property list of maximum and minimum window sizes. 45 | The property keys are `:max-height', `:min-height', `:max-width', 46 | and `:min-width'. They all accept a value of either a 47 | number (integer or floating point) or a function.") 48 | 49 | (defun prot-window--get-window-size (key) 50 | "Extract the value of KEY from `prot-window-window-sizes'." 51 | (when-let ((value (plist-get prot-window-window-sizes key))) 52 | (cond 53 | ((functionp value) 54 | (funcall value)) 55 | ((numberp value) 56 | value) 57 | (t 58 | (error "The value of `%s' is neither a number nor a function" key))))) 59 | 60 | (defun prot-window-select-fit-size (window &rest _) 61 | "Select WINDOW and resize it. 62 | The resize pertains to the maximum and minimum values for height 63 | and width, per `prot-window-window-sizes'. 64 | 65 | Use this as the `body-function' in a `display-buffer-alist' entry." 66 | (select-window window) 67 | (fit-window-to-buffer 68 | window 69 | (prot-window--get-window-size :max-height) 70 | (prot-window--get-window-size :min-height) 71 | (prot-window--get-window-size :max-width) 72 | (prot-window--get-window-size :min-width))) 73 | 74 | (defun prot-window--get-display-buffer-below-or-pop () 75 | "Return list of functions for `prot-window-display-buffer-below-or-pop'." 76 | (list 77 | #'display-buffer-reuse-mode-window 78 | (if (or (prot-common-window-small-p) 79 | (prot-common-three-or-more-windows-p)) 80 | #'display-buffer-below-selected 81 | #'display-buffer-pop-up-window))) 82 | 83 | (defun prot-window-display-buffer-below-or-pop (&rest args) 84 | "Display buffer below current window or pop a new window. 85 | The criterion for choosing to display the buffer below the 86 | current one is a non-nil return value for 87 | `prot-common-window-small-p'. 88 | 89 | Apply ARGS expected by the underlying `display-buffer' functions. 90 | 91 | This as the action function in a `display-buffer-alist' entry." 92 | (let ((functions (prot-window--get-display-buffer-below-or-pop))) 93 | (catch 'success 94 | (dolist (fn functions) 95 | (when (apply fn args) 96 | (throw 'success fn)))))) 97 | 98 | (defun prot-window-shell-or-term-p (buffer &rest _) 99 | "Check if BUFFER is a shell or terminal. 100 | This is a predicate function for `buffer-match-p', intended for 101 | use in `display-buffer-alist'." 102 | (when (string-match-p "\\*.*\\(e?shell\\|v?term\\).*" (buffer-name (get-buffer buffer))) 103 | (with-current-buffer buffer 104 | ;; REVIEW 2022-07-14: Is this robust? 105 | (and (not (derived-mode-p 'message-mode 'text-mode)) 106 | (derived-mode-p 'eshell-mode 'shell-mode 'comint-mode 'fundamental-mode))))) 107 | 108 | (defmacro prot-window-define-full-frame (name &rest args) 109 | "Define command to call ARGS in new frame with `display-buffer-full-frame' bound. 110 | Name the function prot-window- followed by NAME. If ARGS is nil, 111 | call NAME as a function." 112 | (declare (indent 1)) 113 | `(defun ,(intern (format "prot-window-%s" name)) () 114 | ,(format "Call `prot-window-%s' in accordance with `prot-window-define-full-frame'." name) 115 | (interactive) 116 | (let ((display-buffer-alist '((".*" (display-buffer-full-frame))))) 117 | (with-selected-frame (make-frame) 118 | ,(if args 119 | `(progn ,@args) 120 | `(funcall ',name)) 121 | (modify-frame-parameters nil '((buffer-list . nil))))))) 122 | 123 | (defun prot-window--get-shell-buffers () 124 | "Return list of `shell' buffers." 125 | (seq-filter 126 | (lambda (buffer) 127 | (with-current-buffer buffer 128 | (derived-mode-p 'shell-mode))) 129 | (buffer-list))) 130 | 131 | (defun prot-window--get-new-shell-buffer () 132 | "Return buffer name for `shell' buffers." 133 | (if-let ((buffers (prot-window--get-shell-buffers)) 134 | (buffers-length (length buffers)) 135 | ((>= buffers-length 1))) 136 | (format "*shell*<%s>" (1+ buffers-length)) 137 | "*shell*")) 138 | 139 | ;;;###autoload (autoload 'prot-window-shell "prot-window") 140 | (prot-window-define-full-frame shell 141 | (let ((name (prot-window--get-new-shell-buffer))) 142 | (shell name) 143 | (set-frame-name name) 144 | (when-let ((buffer (get-buffer name))) 145 | (with-current-buffer buffer 146 | (add-hook 147 | 'delete-frame-functions 148 | (lambda (_) 149 | ;; FIXME 2023-09-09: Works for multiple frames (per 150 | ;; `make-frame-command'), but not if the buffer is in two 151 | ;; windows in the same frame. 152 | (unless (> (safe-length (get-buffer-window-list buffer nil t)) 1) 153 | (let ((kill-buffer-query-functions nil)) 154 | (kill-buffer buffer)))) 155 | nil 156 | :local))))) 157 | 158 | ;;;###autoload (autoload 'prot-window-coach "prot-window") 159 | (prot-window-define-full-frame coach 160 | (let ((buffer (get-buffer-create "*scratch for coach*"))) 161 | (with-current-buffer buffer 162 | (funcall initial-major-mode)) 163 | (display-buffer buffer) 164 | (set-frame-name "Coach"))) 165 | 166 | ;; REVIEW 2023-06-25: Does this merit a user option? I don't think I 167 | ;; will ever set it to the left. It feels awkward there. 168 | (defun prot-window-scroll-bar-placement () 169 | "Control the placement of scroll bars." 170 | (setq default-frame-scroll-bars 'right) 171 | (set-scroll-bar-mode 'right)) 172 | 173 | (add-hook 'scroll-bar-mode-hook #'prot-window-scroll-bar-placement) 174 | 175 | (defun prot-window-no-minibuffer-scroll-bar (frame) 176 | "Remove the minibuffer scroll bars from FRAME." 177 | (set-window-scroll-bars (minibuffer-window frame) nil nil nil nil :persistent)) 178 | 179 | (add-hook 'after-make-frame-functions 'prot-window-no-minibuffer-scroll-bar) 180 | 181 | (provide 'prot-window) 182 | ;;; prot-window.el ends here 183 | -------------------------------------------------------------------------------- /emacs.d/random-tables-tor.el: -------------------------------------------------------------------------------- 1 | (if (f-file? "~/git/random-table.el/random-table.el") 2 | (require 'random-table "~/git/random-table.el/random-table.el") 3 | (use-package random-table 4 | :straight (:host github :repo "jeremyf/random-table.el"))) 5 | 6 | (setq random-table/reporter #'random-table/reporter/as-insert) 7 | 8 | (random-table/register :name "The One Ring > Lore" 9 | :data '("{The One Ring > Lore @label}{The One Ring > Lore @options}") 10 | :label '(read-string "Open-ended Question: ") 11 | :options '(("Action" . "- Action :: {The One Ring > Lore Table > Action}") 12 | ("Aspect" . "- Aspect :: {The One Ring > Lore Table > Aspect}") 13 | ("Feature" . "- Feature :: {The One Ring > Lore Table > Feature}") 14 | ("Focus" . "- Focus :: {The One Ring > Lore Table > Focus}"))) 15 | 16 | (random-table/register :name "The One Ring > Lore Table > Action" 17 | :private t 18 | :data '("⏿ Abandon" "⏿ Attack" "⏿ Betray" "⏿ Corrupt" "⏿ Defeat" "⏿ Weaken" ;; Eye of Sauron 19 | "Aid" "Arrive" "Await" "Breach" "Break" "Capture" ;; 1 20 | "Change" "Chase" "Command" "Control" "Create" "Defy" ;; 2 21 | "Demand" "Discover" "Disguise" "Endure" "Escape" "Evade" ;; 3 22 | "Explore" "Find" "Focus" "Gather" "Guard" "Guide" ;; 4 23 | "Hide" "Hinder" "Hoard" "Hold" "Hunt" "Journey" ;; 5 24 | "Lead" "Learn" "Leave" "Lose" "Mourn" "Move" ;; 6 25 | "Persist" "Preserve" "Prevent" "Refuse" "Reject" "Remove" ;; 7 26 | "Replenish" "Restore" "Scheme" "Search" "Seize" "Share" ;; 8 27 | "Slay" "Steal" "Summon" "Surrender" "Surround" "Threaten" ;; 9 28 | "Transform" "Trap" "Trick" "Uncover" "Uphold" "Withstand" ;; 10 29 | "ᚠ Believe" "ᚠ Bolster" "ᚠ Defend" "ᚠ Forgive" "ᚠ Resist" "ᚠ Strengthen")) 30 | 31 | (random-table/register :name "The One Ring > Lore Table > Aspect" 32 | :private t 33 | :data '("⏿ Corrupted" "⏿ Cruel" "⏿ Deceptive" "⏿ Fell" "⏿ Ruined" "⏿ Treacherous" ;; Eye of Sauron 34 | "Active" "Ancient" "Bold" "Bright" "Broken" "Cheerless" ;; 1 35 | "Cold" "Concealed" "Dangerous" "Dark" "Dead" "Defended" ;; 2 36 | "Desolate" "Destroyed" "Dreadful" "Empty" "Evil" "Faded" ;; 3 37 | "Far-reaching" "Fierce" "Foreboding" "Forgotten" "Fragile" "Ghastly" ;; 4 38 | "Gloomy" "Growing" "Hidden" "Ill-fated" "Impenetrable" "Inspiring" ;; 5 39 | "Isolated" "Lofty" "Lost" "Menacing" "Mighty" "Mysterious" ;; 6 40 | "Noble" "Obstructed" "Old" "Ominous" "Open" "Peaceful" ;; 7 41 | "Restored" "Sheltered" "Silent" "Simple" "Small" "Sombre" ;; 8 42 | "Stony" "Stout" "Stricken" "Stubborn" "Twisted" "Unnatural" ;; 9 43 | "Veiled" "Vigorous" "Weary" "Wild" "Wretched" "Young" ;; 10 44 | "ᚠ Flourishing" "ᚠ Beautiful" "ᚠ Good" "ᚠ Kind" "ᚠ Gentle" "ᚠ Wondrous")) 45 | 46 | (random-table/register :name "The One Ring > Lore Table > Feature" 47 | :private t 48 | :data '("⏿ Darkness" "⏿ Ruin" "⏿ Blood" "⏿ Bones" "⏿ Corpse" "⏿ Trap" ;; Eye of Sauron 49 | "Archive" "Armament" "Barricade" "Battlefield" "Bridge" "Cave-in" 50 | "Chill" "Container" "Creature" "Dead-end" "Debris" "Doorway" 51 | "Dust" "Echoes" "Encampment" "Enchantment" "Excavation" "Fire" 52 | "Fissure" "Fortification" "Gate" "Ghost" "Heat" "Heights" 53 | "Hideaway" "Illusion" "Inscription" "Lair" "Machinery" "Mist" 54 | "Monument" "Nest" "Opening" "Person" "Pillar" "Pit" 55 | "Provisions" "Puzzle" "Rubbish" "Runes" "Scratches" "Silence" 56 | "Smoke" "Sound" "Stairs" "Stench" "Stone" "Tomb" 57 | "Tool" "Trail" "Vault" "Viewpoint" "Vision" "Voice" 58 | "Wall" "Warren" "Water" "Whispers" "Wind" "Wood" 59 | "ᚠ Artefact" "ᚠ Artwork" "ᚠ Illumination" "ᚠ Plants" "ᚠ Shelter" "ᚠ Treasure")) 60 | 61 | (random-table/register :name "The One Ring > Lore Table > Focus" 62 | :private t 63 | :data '("⏿ Curse" "⏿ Despair" "⏿ Enemy" "⏿ Fear" "⏿ Shadow" "⏿ War" ;; Eye of Sauron 64 | "Battle" "Border" "Burden" "Council" "Court" "Creature" ;; 1 65 | "Darkness" "Death" "Defence" "Depths" "Doubt" "Dreams" ;; 2 66 | "Fate" "Fire" "Folk" "Followers" "Greed" "Haven" ;; 3 67 | "History" "Honour" "Journey" "Kindred" "Knowledge" "Land" ;; 4 68 | "Leader" "Legend" "Life" "Light" "Luck" "Memory" ;; 5 69 | "Message" "Might" "Nature" "Pain" "Path" "Patron" ;; 6 70 | "Peril" "Plan" "Power" "Prophecy" "Quest" "Refuge" ;; 7 71 | "Riddle" "Ruins" "Rumour" "Secret" "Skill" "Song" ;; 8 72 | "Story" "Strength" "Time" "Tool" "Treasure" "Trust" ;; 9 73 | "Truth" "Vengeance" "Wealth" "Weapon" "Wilds" "Wish" ;; 10 74 | "ᚠ Courage" "ᚠ Duty" "ᚠ Fellowship" "ᚠ Hope" "ᚠ Love" "ᚠ Peace")) 75 | 76 | (random-table/register :name "The One Ring > Chance Meeting" 77 | :data '("\n\t- Encounter :: {The One Ring > Chance Meeting > Encounter}\n\t- Temperament :: {The One Ring > Chance Meeting > Termperament}\n\t- Focus :: {The One Ring > Chance Meeting > Focus}")) 78 | 79 | (random-table/register :name "The One Ring > Chance Meeting > Encounter" 80 | :private t 81 | :data '("A Deadly Foe" "Large Group of Adversaries" "Small Group of Adversaries" 82 | "Small Group of Adversaries" "Small Group of Adversaries" "Potential Adversaries" 83 | "Travelers" "Travelers" "Merchants" 84 | "Merchants" "Friendly Faces" "A Known Ally")) 85 | 86 | (random-table/register :name "The One Ring > Chance Meeting > Termperament" 87 | :private t 88 | :data '("Raging" "Paranoid" "Shadowed" "Weary" "Wary" "Lost" 89 | "Mysterious" "Determined" "Peaceful" "Cheerful" "Corrupted" "Inspiring")) 90 | 91 | (random-table/register :name "The One Ring > Chance Meeting > Focus" 92 | :private t 93 | :data '("Engaged in Combat" "Lying in Wait" "Hunting Something" 94 | "Engaged in Conversation" "On a Long March" "Wandering Aimlessly" 95 | "Exploring a Ruin" "Setting up Camp" "Eating a Meal" 96 | "Recovering from Battle" "In a Bout of Madness" "Resting Peacfully")) 97 | 98 | ;; Assumption is that we're not interested 99 | (defconst random-table/roller/one-ring-favourability 100 | '(("Favoured" . (lambda () (max (random 12) (random 12)))) 101 | ("Neutral" . (lambda () (random 12))) 102 | ("Ill-Favoured" . (lambda () (min (random 12) (random 12))))) 103 | "Favourability options and associated roller 104 | 105 | The Eye is 0 106 | Gandalf is 11") 107 | 108 | (defun random-table/roller/one-ring-event (&optional table) 109 | (funcall (random-table/completing-read/alist 110 | "Favourability: " 111 | random-table/roller/one-ring-favourability 112 | nil 113 | t))) 114 | 115 | (random-table/register :name "The One Ring > Event" 116 | :roller #'random-table/roller/one-ring-event 117 | ;; TODO Add a favorability selector 118 | :data '((0 . "Terrible Misfortune: {The One Ring > Event > Terrible Misfortune}\n\t- Consequence :: If the roll fails, the target is Wounded.\n\t- Fatigue :: 3") 119 | (1 . "Despair: {The One Ring > Event > Despair}\n\t- Consequence :: If the roll fails, gain 2 Shadow points (Dread).\n\t- Fatigue :: 2") 120 | ((2 . 3) . "Ill Choices: {The One Ring > Event > Ill Choices}\n\t- Consequence :: If the roll fails, gain 1 Shadow point (Dread).\n\t- Fatigue :: 2") 121 | ((4 . 7) . "Mishap: {The One Ring > Event > Mishap}\n\t- Consequence :: If the roll fails, add 1 day to the length of the journey, and gain 1 additional Fatigue.\n\t- Fatigue :: 2") 122 | ((8 . 9) . "Short Cut: {The One Ring > Event > Short Cut}\n\t- Consequence :: If the roll succeeds, reduce the length of the journey by 1 day.\n\t- Fatigue :: 1") 123 | (10 . "Chance Meeting: {The One Ring > Event > Chance Meeting}\n\t- Consequence :: If the roll succeeds, no Fatigue is gained, and you may envision a favourable encounter.\n\t- Fatigue :: 1") 124 | (11 . "Joyful Sight: {The One Ring > Event > Joyful Sight}\n\t- Consequence :: If the roll succeeds, regain 1 Hope.\n\t- Fatigue :: 0"))) 125 | 126 | (random-table/register :name "The One Ring > Event > Terrible Misfortune" 127 | :private t 128 | :data '("Dire confrontation\n\t- Task :: Noteworthy Encounter" 129 | "Rival Predator\n\t- Task :: HUNTING to avoid becoming the hunted" 130 | "Violent weather\n\t- Task :: EXPLORE to find shelter" 131 | "Hidden hazard\n\t- Task :: AWARENESS to avoid stumbling into danger" 132 | "Dangerous terrain\n\t- Task :: EXPLORE to find a safer route" 133 | "Stalking enemy\n\t- Task :: AWARENESS to spot the foul presence")) 134 | (random-table/register :name "The One Ring > Event > Despair" 135 | :private t 136 | :data '("Servants of the Enemy\n\t- Task :: Noteworthy Encounter" 137 | "Torrential weather\n\t- Task :: EXPLORE to find the least exposed path" 138 | "Nightmarish presence\n\t- Task :: AWARENESS to sense the danger" 139 | "Fading vigour\n\t- Task :: HUNTING to gain sustenance" 140 | "Corrupted site\n\t- Task :: EXPLORE to find your way out" 141 | "Grisly scene or foreboding portent\n\t- Task :: AWARENESS to be forewarned")) 142 | (random-table/register :name "The One Ring > Event > Mishap" 143 | :private t 144 | :data '("Sparse wildlife\n\t- Task :: HUNTING to forage what you can" 145 | "Lost direction\n\t- Task :: EXPLORE to find your way" 146 | "Obstructed path\n\t- Task :: AWARENESS to spot a way around" 147 | "Elusive quarry\n\t- Task :: HUNTING to track it down" 148 | "Rough terrain\n\t- Task :: EXPLORE to safely traverse" 149 | "Wandering enemies\n\t- Task :: AWARENESS to sense their coming")) 150 | (random-table/register :name "The One Ring > Event > Ill Choices" 151 | :private t 152 | :data '("Mismanaged provisions\n\t- Task :: HUNTING to replenish stores" 153 | "Wayward path\n\t- Task :: EXPLORE to retrace your steps" 154 | "Overlooked hazard\n\t- Task :: AWARENESS to escape safely" 155 | "Lost quarry\n\t- Task :: HUNTING to follow its tracks" 156 | "Disorienting environs\n\t- Task :: EXPLORE to find your way" 157 | "Haunting visions\n\t- Task :: AWARENESS to over- come darkness")) 158 | (random-table/register :name "The One Ring > Event > Short Cut" 159 | :private t 160 | :data '("Game trail\n\t- Task :: HUNTING to traverse the path" 161 | "Secluded path\n\t- Task :: EXPLORE to navigate the wilds" 162 | "Helpful tracks\n\t- Task :: AWARENESS to follow the tracks" 163 | "Animal guide\n\t- Task :: HUNTING to follow at a distance" 164 | "Favourable weather\n\t- Task :: EXPLORE to make the most of it" 165 | "Familiar waypoint\n\t- Task :: AWARENESS to recognize the landmark")) 166 | (random-table/register :name "The One Ring > Event > Chance Meeting" 167 | :private t 168 | :data '("Lone hunter\n\t- Task :: HUNTING to trade stories" 169 | "Fellow traveller\n\t- Task :: EXPLORE to learn about the path ahead" 170 | "Discreet watcher\n\t- Task :: AWARENESS to spot them" 171 | "Noble beast\n\t- Task :: HUNTING to commune" 172 | "Secluded encampment\n\t- Task :: EXPLORE to find your way off the beaten path" 173 | "Auspicious gathering\n\t- Task :: Noteworthy Encounter")) 174 | (random-table/register :name "The One Ring > Event > Joyful Sight" 175 | :private t 176 | :data '("Majestic creatures\n\t- Task :: HUNTING to observe without startling them" 177 | "Inspiring vista\n\t- Task :: EXPLORE to reach a vantage point" 178 | "Benevolent being\n\t- Task :: AWARENESS to sense their presence" 179 | "Abundant foraging\n\t- Task :: HUNTING to replenish your rations" 180 | "Ancient monument\n\t- Task :: AWARENESS to recognize its significance" 181 | "Peaceful sanctuary\n\t- Task :: Noteworthy Encounter")) 182 | 183 | (random-table/register :name "The One Ring > Chamber" 184 | :data '("- Chamber Type :: {The One Ring > Chamber > Type}\n- Condition :: {The One Ring > Chamber > Condition}\n- Appearance :: {The One Ring > Chamber > Appearance}\n- Challenge :: {The One Ring > Chamber > Challenge}")) 185 | 186 | (random-table/register :name "The One Ring > Chamber > Challenge" 187 | :private t 188 | :data '("Combat" "None (Desolation)" "Athletics" "Battle" "Enhearten" "Healing" "Hunting" "Lore" "Riddle" "Stealth" "None (Foreshadowing)" "Token of Hope")) 189 | 190 | (random-table/register :name "The One Ring > Chamber > Appearance" 191 | :private t 192 | :data '("Shunned" "Unfinished" "Austere" "Ancient" "Simple" "Heroic" "Homely" "Richly Decorated" "Elven" "Fortified" "Natural" "Enchanted")) 193 | 194 | (random-table/register :name "The One Ring > Chamber > Condition" 195 | :private t 196 | :data '("Held by foes" "Blocked" "Suspiciously Intact" "Flooded" "Utterly ruined" "Goblin-gnnawed" "Shattered by earthquake" "Burnt" "Despoiled" "Ruined by the passag eof time" "Mostly intact" "A safe place to rest")) 197 | 198 | (random-table/register :name "The One Ring > Chamber > Type" 199 | :private t 200 | :data '("Orc-nest" "Utility" "Storeroom" "Small Dwelling" "Stairs" "[Well/Watercourse]" "[Guard Post/Armoury]" "Forge" "Workshop" "Large Dwelling" "Civic Building" "Great Hall")) 201 | -------------------------------------------------------------------------------- /emacs.d/work.el: -------------------------------------------------------------------------------- 1 | (dir-locals-set-class-variables 2 | 'pcloud-cli 3 | '((nil . ((eglot-workspace-configuration . 4 | ( 5 | :elixirLS ( 6 | :projectDir "pcloud_cli_integration/") 7 | )))))) 8 | (dir-locals-set-class-variables 9 | 'go-lang 10 | '((nil . ((projectile-git-fd-args . 11 | "-H -0 -tf --strip-cwd-prefix -c never -E vendor/ -E pkg/ -E docs/ -E .git"))))) 12 | 13 | (dir-locals-set-directory-class 14 | "~/git/converge-cloud/marketplace-provider" 'go-lang) 15 | 16 | (dir-locals-set-directory-class 17 | "~/git/converge-cloud/morpho-pcloud-cli/" 'pcloud-cli) 18 | 19 | (dir-locals-set-directory-class 20 | "~/git/converge-cloud/concierge" 'go-lang) 21 | 22 | (dir-locals-set-directory-class 23 | "~/git/converge-cloud/morpho-account-service" 'go-lang) 24 | 25 | (dir-locals-set-directory-class 26 | "~/git/converge-cloud/morpho-service-broker" 'go-lang) 27 | 28 | 29 | ;; At my current employer, I have quarterly managed business objectives 30 | ;; (MBOs). These focal activities involve: initiation, tracking work, 31 | ;; and completion (with supporting documentation). 32 | ;; 33 | ;; **Initiation** :: Writing the stated objective with narrative around 34 | ;; the goal, measurement, and artifacts. 35 | ;; 36 | ;; **Tracking Work** :: As I work through these objectives, I'm 37 | ;; recording and tracking tasks towards the larger MBO. Some tasks 38 | ;; become blocked and I need to work towards their resolution by 39 | ;; providing sustained energy up and out; to get help with my team. 40 | ;; 41 | ;; **Completion** :: This involves providing documentation demonstrating 42 | ;; the completion of the MBO. I can leverage the tracking information 43 | ;; to provide the documentation. 44 | ;; 45 | ;; The two capture template letters reflect what I'm using for my 46 | ;; personal (non-work related) notes. So I'm hoping to piggy back on 47 | ;; that muscle memory as I adopt these tracking approaches. 48 | (defvar jf/work/filename-for-mbos 49 | (denote-get-path-by-id "20250117T101521") 50 | "Where I put my MBOs.") 51 | 52 | (add-to-list 'org-capture-templates 53 | '("T" "Add to task for MBO" 54 | plain (file+function 55 | jf/work/filename-for-mbos 56 | jf/work/position-at-end-of-mbo-task-entity) 57 | "%T :: %?" 58 | :empty-lines-before 1 59 | :empty-lines-after 1 60 | :clock-in t 61 | :clock-resume t)) 62 | 63 | (add-to-list 'org-capture-templates 64 | '("t" "New task for MBO" 65 | entry (file+function 66 | jf/work/filename-for-mbos 67 | jf/work/position-at-start-of-mbo) 68 | "TODO %^{Task} :tasks:\n:PROPERTIES:\n:CUSTOM_ID: %(org-id-new)\n:END:\n%?" 69 | :empty-lines-before 1 70 | :empty-lines-after 1 71 | :clock-in t 72 | :clock-resume t)) 73 | 74 | (add-to-list 'org-capture-templates 75 | '("j" "Journal entry for MBO" 76 | plain (file+function 77 | jf/work/filename-for-mbos 78 | jf/work/position-in-mbo-journal-entry) 79 | "%T :: %?" 80 | :empty-lines-before 1 81 | :empty-lines-after 1 82 | :clock-in t 83 | :clock-resume t)) 84 | 85 | (defvar jf/work/org-map-entries-mbo-task-filter 86 | "+LEVEL=4+mbos+tasks-journals+TODO!=\"DONE\"" 87 | "The default filter for collecting MBO tasks.") 88 | 89 | (defun jf/work/position-in-mbo-journal-entry () 90 | "Position point just before the selected journal. 91 | 92 | Included to allow the re-use of logic for different 3rd level tasks." 93 | (let ((jf/work/org-map-entries-mbo-task-filter 94 | "+LEVEL=4+mbos+tasks+journals")) 95 | (jf/work/position-at-end-of-mbo-task-entity))) 96 | 97 | (defun jf/work/position-at-end-of-mbo-task-entity () 98 | "Position point just before content end of MBO task. 99 | 100 | For inserting plain text." 101 | (let* ((incomplete-mbo-tasks 102 | (org-map-entries 103 | (lambda () 104 | (let* ((task 105 | (org-element-at-point)) 106 | (mbo 107 | (car (org-element-lineage task))) 108 | (quarter 109 | (caddr (org-element-lineage task)))) 110 | (cons 111 | (concat 112 | (propertize 113 | (org-element-property :title quarter) 114 | 'face 'org-level-1) 115 | (propertize 116 | " > " 117 | 'face 'consult-separator) 118 | (propertize 119 | (org-element-property :title mbo) 120 | 'face 'org-level-3) 121 | (propertize 122 | " > " 123 | 'face 'consult-separator) 124 | (propertize 125 | (org-element-property :title task) 126 | 'face 'org-level-4) 127 | ) 128 | (org-element-property :contents-end task)))) 129 | jf/work/org-map-entries-mbo-task-filter)) 130 | (task 131 | (completing-read 132 | "Task: " incomplete-mbo-tasks nil t))) 133 | ;; The contents-end of the task is the begining of the line that 134 | ;; contains the next heading. Which the capture process for a 135 | ;; "plain" item then interprets as "add an item to the current 136 | ;; heading". Not ideal, so move point backwards one character. 137 | (goto-char 138 | (1- (alist-get task incomplete-mbo-tasks nil nil #'string=))))) 139 | 140 | (defun jf/work/position-at-start-of-mbo () 141 | "Position point at start of MBO. 142 | 143 | For inserting entity." 144 | (let* ((incomplete-mbos 145 | (org-map-entries 146 | (lambda () 147 | (let* ((mbo 148 | (org-element-at-point)) 149 | (quarter 150 | (cadr (org-element-lineage mbo)))) 151 | (cons 152 | (concat 153 | (propertize 154 | (org-element-property :title quarter) 155 | 'face 'org-level-1) 156 | (propertize 157 | " > " 158 | 'face 'consult-separator) 159 | (propertize 160 | (org-element-property :title mbo) 161 | 'face 'org-level-3) 162 | ) 163 | (org-element-property :begin mbo)))) 164 | "+LEVEL=3+mbos-noexport+TODO!=\"DONE\"")) 165 | (mbo 166 | (completing-read 167 | "MBO: " incomplete-mbos nil t))) 168 | (goto-char (alist-get mbo incomplete-mbos nil nil #'string=)))) 169 | 170 | (with-eval-after-load 'dape 171 | ;; Add Go debug configuration 172 | ;; `attach host "127.0.0.1" port 2345 :mode "remote" :type "go"` 173 | (add-to-list 'dape-configs 174 | `(sb-attach-to-air 175 | modes (go-mode go-ts-mode) 176 | port 2345 177 | host "127.0.0.1" 178 | :type "go" 179 | :mode "remote" 180 | :request "attach"))) 181 | -------------------------------------------------------------------------------- /icons/bookish-gnu-alt.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyf/dotemacs/ab77d4c06c4651ab23137e5b022a40d69a3889a4/icons/bookish-gnu-alt.icns -------------------------------------------------------------------------------- /icons/bullish-gnu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyf/dotemacs/ab77d4c06c4651ab23137e5b022a40d69a3889a4/icons/bullish-gnu.png -------------------------------------------------------------------------------- /icons/gnu-standing-on-a-clown-sized-pile-of-books.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyf/dotemacs/ab77d4c06c4651ab23137e5b022a40d69a3889a4/icons/gnu-standing-on-a-clown-sized-pile-of-books.icns -------------------------------------------------------------------------------- /icons/gnu-standing-on-piles-of-paper.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyf/dotemacs/ab77d4c06c4651ab23137e5b022a40d69a3889a4/icons/gnu-standing-on-piles-of-paper.icns -------------------------------------------------------------------------------- /icons/purple-gnu.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyf/dotemacs/ab77d4c06c4651ab23137e5b022a40d69a3889a4/icons/purple-gnu.icns -------------------------------------------------------------------------------- /icons/purple-gnu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyf/dotemacs/ab77d4c06c4651ab23137e5b022a40d69a3889a4/icons/purple-gnu.jpg -------------------------------------------------------------------------------- /icons/stained-glass-gnu.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremyf/dotemacs/ab77d4c06c4651ab23137e5b022a40d69a3889a4/icons/stained-glass-gnu.icns -------------------------------------------------------------------------------- /lib/jf-horizontal-printing.setup: -------------------------------------------------------------------------------- 1 | #+LATEX_CLASS: jf/landscape 2 | #+LATEX_HEADER: \usepackage[linktocpage=true]{hyperref} 3 | #+LATEX_HEADER: \usepackage[french,english]{babel} 4 | #+LATEX_HEADER: \usepackage[a4paper,top=3cm,bottom=3cm]{geometry} 5 | #+LATEX_HEADER: \usepackage{minimalist} 6 | #+LATEX_HEADER: \usepackage{fontspec} 7 | #+LATEX_HEADER: \usepackage{caption} \captionsetup{labelfont=bf,font={sf,small}} 8 | #+LATEX_HEADER: \setmainfont{TeX Gyre Pagella} 9 | #+LATEX_HEADER: \usepackage{enumitem} \setlist{nosep} 10 | #+LATEX_HEADER: \usepackage{longtable} 11 | #+LATEX_HEADER: \usepackage{microtype} 12 | #+LATEX_HEADER: \AtBeginEnvironment{longtable}{\footnotesize} 13 | #+LATEX_HEADER: \usepackage[marginal,hang]{footmisc} 14 | #+LATEX_HEADER: \usepackage{relsize,etoolbox} 15 | #+LATEX_HEADER: \AtBeginEnvironment{quote}{\smaller} 16 | #+LATEX_HEADER: \AtBeginEnvironment{tabular}{\smaller} 17 | #+LATEX_HEADER: \usepackage[printonlyused,nohyperlinks]{acronym} 18 | #+LATEX_HEADER: \usepackage[marginal,hang]{footmisc} 19 | #+LATEX_HEADER: \usepackage{mathabx} 20 | #+LATEX_HEADER: \usepackage{multicol} 21 | #+LATEX_HEADER: \setlength\columnsep{20pt} 22 | #+LATEX_HEADER: \hypersetup{colorlinks=true, linkcolor=blue, filecolor=magenta, urlcolor=cyan} 23 | #+LATEX_HEADER: \tolerance=1000 24 | #+LATEX_HEADER: \usepackage{float} 25 | #+LATEX_HEADER: \usepackage{rotating} 26 | #+LATEX_HEADER: \usepackage{sectsty} 27 | #+LATEX_HEADER: \usepackage{titlesec} 28 | #+LATEX_HEADER: \titleformat{\section}{\normalfont\fontsize{12}{18}\bfseries}{\thesection}{1em}{} 29 | #+LATEX_HEADER: \setcounter{secnumdepth}{1} 30 | #+OPTIONS: toc:nil 31 | 32 | # If this is a header; it will affect the table of contents. But as a LATEX 33 | # command, it happens in the processing order of the org file. 34 | #+LATEX: \let\oldsection\section 35 | #+LATEX: \renewcommand{\section}[1]{\newpage\end{multicols}\oldsection{#1}\begin{multicols}{2}} 36 | #+LATEX: \let\oldhref\href 37 | #+LATEX: \renewcommand{\href}[2]{\oldhref{#1}{#2}\footnote{\url{#1}}} 38 | -------------------------------------------------------------------------------- /lib/jf-journal.setup: -------------------------------------------------------------------------------- 1 | #+LATEX_CLASS: jf/landscape 2 | #+LATEX_HEADER: \usepackage[linktocpage=true]{hyperref} 3 | #+LATEX_HEADER: \usepackage[french,english]{babel} 4 | #+LATEX_HEADER: \usepackage[a4paper,top=3cm,bottom=3cm]{geometry} 5 | #+LATEX_HEADER: \usepackage{minimalist} 6 | #+LATEX_HEADER: \usepackage{fontspec} 7 | #+LATEX_HEADER: \usepackage{caption} \captionsetup{labelfont=bf,font={sf,small}} 8 | #+LATEX_HEADER: \setmainfont{TeX Gyre Pagella} 9 | #+LATEX_HEADER: \usepackage{enumitem} \setlist{nosep} 10 | #+LATEX_HEADER: \usepackage{longtable} 11 | #+LATEX_HEADER: \usepackage{microtype} 12 | #+LATEX_HEADER: \AtBeginEnvironment{longtable}{\footnotesize} 13 | #+LATEX_HEADER: \usepackage[marginal,hang]{footmisc} 14 | #+LATEX_HEADER: \usepackage{relsize,etoolbox} 15 | #+LATEX_HEADER: \AtBeginEnvironment{quote}{\smaller} 16 | #+LATEX_HEADER: \AtBeginEnvironment{tabular}{\smaller} 17 | #+LATEX_HEADER: \usepackage[printonlyused,nohyperlinks]{acronym} 18 | #+LATEX_HEADER: \usepackage[marginal,hang]{footmisc} 19 | #+LATEX_HEADER: \usepackage{mathabx} 20 | #+LATEX_HEADER: \usepackage{multicol} 21 | #+LATEX_HEADER: \setlength\columnsep{20pt} 22 | #+LATEX_HEADER: \hypersetup{colorlinks=true, linkcolor=blue, filecolor=magenta, urlcolor=cyan} 23 | #+LATEX_HEADER: \tolerance=1000 24 | #+LATEX_HEADER: \usepackage{float} 25 | #+LATEX_HEADER: \usepackage{rotating} 26 | #+LATEX_HEADER: \usepackage{sectsty} 27 | #+LATEX_HEADER: \usepackage{titlesec} 28 | #+LATEX_HEADER: \titleformat{\section}{\normalfont\fontsize{12}{18}\bfseries}{\thesection}{1em}{} 29 | #+LATEX_HEADER: \setcounter{secnumdepth}{1} 30 | #+OPTIONS: toc:3 tags:nil 31 | 32 | #+LATEX: \begin{multicols}{2} 33 | #+LATEX: \let\oldhref\href 34 | #+LATEX: \renewcommand{\href}[2]{\oldhref{#1}{#2}\footnote{\url{#1}}} 35 | -------------------------------------------------------------------------------- /lib/jf-two-column.latex.setup: -------------------------------------------------------------------------------- 1 | #+LATEX_CLASS: jf/article 2 | #+LATEX_CLASS_OPTIONS: [11pt] 3 | #+LATEX_HEADER: \usepackage[linktocpage=true]{hyperref} 4 | #+LATEX_HEADER: \usepackage[french,english]{babel} 5 | #+LATEX_HEADER: \usepackage[a4paper,top=3cm,bottom=3cm]{geometry} 6 | #+LATEX_HEADER: \usepackage{minimalist} 7 | #+LATEX_HEADER: \usepackage{fontspec} 8 | #+LATEX_HEADER: \usepackage{caption} \captionsetup{labelfont=bf,font={sf,small}} 9 | #+LATEX_HEADER: \setmainfont{TeX Gyre Pagella} 10 | #+LATEX_HEADER: \usepackage{enumitem} \setlist{nosep} 11 | #+LATEX_HEADER: \usepackage{longtable} 12 | #+LATEX_HEADER: \usepackage{microtype} 13 | #+LATEX_HEADER: \AtBeginEnvironment{longtable}{\footnotesize} 14 | #+LATEX_HEADER: \usepackage[marginal,hang]{footmisc} 15 | #+LATEX_HEADER: \usepackage{relsize,etoolbox} 16 | #+LATEX_HEADER: \AtBeginEnvironment{quote}{\smaller} 17 | #+LATEX_HEADER: \AtBeginEnvironment{tabular}{\smaller} 18 | #+LATEX_HEADER: \usepackage[printonlyused,nohyperlinks]{acronym} 19 | #+LATEX_HEADER: \usepackage[marginal,hang]{footmisc} 20 | #+LATEX_HEADER: \usepackage{multicol} 21 | #+LATEX_HEADER: \setlength\columnsep{20pt} 22 | #+LATEX_HEADER: \hypersetup{colorlinks=true, linkcolor=blue, filecolor=magenta, urlcolor=cyan} 23 | #+LATEX_HEADER: \tolerance=1000 24 | #+LATEX_HEADER: \usepackage{float} 25 | #+LATEX_HEADER: \usepackage{rotating} 26 | #+LATEX_HEADER: \usepackage{sectsty} 27 | #+LATEX_HEADER: \usepackage{titlesec} 28 | #+LATEX_HEADER: \titleformat{\section}{\normalfont\fontsize{12}{18}\bfseries}{\thesection}{1em}{} 29 | #+LATEX_HEADER: \setcounter{secnumdepth}{1} 30 | 31 | # If this is a header; it will affect the table of contents. But as a LATEX 32 | # command, it happens in the processing order of the org file. 33 | #+LATEX: \let\oldsection\section 34 | #+LATEX: \renewcommand{\section}[1]{\newpage\end{multicols}\oldsection{#1}\begin{multicols}{2}} 35 | -------------------------------------------------------------------------------- /lib/org-macros.setup: -------------------------------------------------------------------------------- 1 | # -*- mode: org; -*- 2 | -------------------------------------------------------------------------------- /lib/treesit.rb: -------------------------------------------------------------------------------- 1 | class Stuff 2 | ## 3 | # @param hello [Object] When the work type does not have =part_of= we When 4 | # thework type does not have =part_of= weWhen the work type does not have 5 | # =part_of= we When the work type does not have =part_of= we 6 | # @param world [Object] When the work type does not have =part_of= we When the 7 | # work type does not have =part_of= weWhen the work type does not have 8 | # =part_of= we When the work type does not have =part_of= we 9 | def initialize(hello:, world:) 10 | self.hello = hello 11 | self.world = world 12 | end 13 | 14 | attr_accessor :hello, :world 15 | private :hello, :world 16 | private :hello=, :world= 17 | end 18 | -------------------------------------------------------------------------------- /random-tables/daytrippers/mission-generator.txt: -------------------------------------------------------------------------------- 1 | ;main 2 | [mission-generator.mission-type] 3 | 4 | ;mission-type 5 | 10, Exploration via [mission-generator.node-type-exploration-rescue] 6 | 5, Emergency/Rescue via [mission-generator.node-type-exploration-rescue] 7 | 6, Sightseeing/Tourism 8 | 9, Survey/Fact-Finding 9 | 3, Acquisition/Trade 10 | 3, Politics/Diplomacy 11 | 12 | ;node-type-exploration-rescue 13 | 1, Multiversal Chao 14 | 9, Unknown Planet 15 | 11, Dream World 16 | 9, Alternate Earth 17 | 3, Time Travel 18 | 3, Known Planet -------------------------------------------------------------------------------- /templates: -------------------------------------------------------------------------------- 1 | ;; -*- mode: lisp -*- 2 | ;;; Commentary 3 | ;; 4 | ;; Organize this file by mode then by abbreviation. Multi-modes have their own 5 | ;; section 6 | 7 | ;;; Single Mode Entries 8 | 9 | ;;;; emacs-lisp 10 | emacs-lisp-mode 11 | 12 | (autoload ";;;###autoload") 13 | (pt "(point)") 14 | (lambda "(lambda (" p ")" n> r> ")") 15 | (var "(defvar " p "\n \"" p "\")") 16 | (local "(defvar-local " p "\n \"" p "\")") 17 | (const "(defconst " p "\n \"" p "\")") 18 | (custom "(defcustom " p "\n \"" p "\"" n> ":type '" p ")") 19 | (face "(defface " p " '((t :inherit " p "))\n \"" p "\")") 20 | (group "(defgroup " p " nil\n \"" p "\"" n> ":group '" p n> ":prefix \"" p "-\")") 21 | (macro "(defmacro " p " (" p ")\n \"" p "\"" n> r> ")") 22 | (alias "(defalias '" p " '" p ")") 23 | (fun "(defun " p " (" p ")\n \"" p "\"" n> r> ")") 24 | (iflet "(if-let (" p ")" n> r> ")") 25 | (whenlet "(when-let (" p ")" n> r> ")") 26 | (iflet* "(if-let* (" p ")" n> r> ")") 27 | (whenlet* "(when-let* (" p ")" n> r> ")") 28 | (andlet* "(and-let* (" p ")" n> r> ")") 29 | (cond "(cond" n "(" q "))" >) 30 | (pcase "(pcase " (p "scrutinee") n "(" q "))" >) 31 | (let "(let (" p ")" n> r> ")") 32 | (let* "(let* (" p ")" n> r> ")") 33 | (rec "(letrec (" p ")" n> r> ")") 34 | (dotimes "(dotimes (" p ")" n> r> ")") 35 | (dolist "(dolist (" p ")" n> r> ")") 36 | (loop "(cl-loop for " p " in " p " do" n> r> ")") 37 | (command "(defun " p " (" p ")\n \"" p "\"" n> "(interactive" p ")" n> r> ")") 38 | (advice "(defun " (p "adv" name) " (&rest app)" n> p n> "(apply app))" n> 39 | "(advice-add #'" (p "fun") " " (p ":around") " #'" (s name) ")") 40 | (package ";;; " (file-name-base (or (buffer-file-name) (buffer-name))) " --- " p " -*- lexical-binding: t -*-" 41 | n n 42 | ";; Copyright (C) " (format-time-string "%Y") " Jeremy Friesen" n 43 | ";; Author: Jeremy Friesen " n n 44 | ";; This file is NOT part of GNU Emacs." n 45 | ";;; Commentary:" n p n 46 | ";;; Code:" n p n 47 | "(provide '" (file-name-base (or (buffer-file-name) (buffer-name))) ")" n 48 | ";;; " (file-name-nondirectory (or (buffer-file-name) (buffer-name))) " ends here" n) 49 | 50 | ;;;; fundamental-mode 51 | fundamental-mode 52 | 53 | ;;;; markdown-mode 54 | markdown-mode markdown-ts-mode 55 | 56 | (ghcallout "> [!" (p (completing-read "Callout Type:" '("NOTE" "TIP" "IMPORTANT" "CAUTION" "WARNING") nil t)) "]" n "> " q) 57 | 58 | ;;;; org-mode 59 | org-mode 60 | 61 | (utoday "[[date:" (format-time-string "%Y-%m-%d") "][Today]]") 62 | (ltoday "[[date:" (format-time-string "%Y-%m-%d") "][today]]") 63 | (beamer 64 | n "#+OPTIONS: H:2 num:t" 65 | n "#+BEAMER_THEME: Madrid" 66 | n "#+LATEX_CLASS: beamer" 67 | n "#+TOC: headlines [currentsection]" 68 | n "#+STARTUP: beamer" 69 | n "#+latex_header: \\AtBeginSection[]{\\begin{frame}\\frametitle{Topic}\\tableofcontents[currentsection]\\end{frame}}" n) 70 | 71 | (eg "([[abbr:20230824T082549][e.g.]], " q ")") 72 | (aka "([[abbr:20221009T115519][a.k.a.]] " q ")") 73 | (dnd "[[abbr:20221009T115753][D&D]] " q ) 74 | (ie "[[abbr:20230825T130802][i.e.]] " q "") 75 | (clocktable "#+BEGIN: clocktable :scope file :tcolumns 1 :block untilnow :narrow 80! :step year :stepskip0 t :fileskip0 t :filetitle t\n#+END:") 76 | ;; (lore24-transclude "#+TRANSCLUDE: [[id:" p "]] :only-contents :exclude-elements \"drawer keyword headline\"") 77 | 78 | (ntable "#+CAPTION: " (p "Caption: " caption) n "#+NAME: " (jf/tor-convert-text-to-slug caption) n "|" q "|" n) 79 | ;; (tsomb-date (p "Day: " day t) "[[date:1627-05-" (format "%02d" (string-to-number day)) "][May " day "]]" q) 80 | 81 | (local-var "# -*- " (p "Variable name: ")": " (p "Value: ") "; -*-") 82 | (macro-cite "{{{cite(" (p (completing-read "Cite: " (jf/org-macro-value-list "cite"))) ")}}}") 83 | (macro-keyboard "{{{kbd(" (p (completing-read "Keyboard: " (jf/org-macro-value-list "kbd"))) ")}}}") 84 | (macro-emphatic "{{{em(" (p (completing-read "Emphasis: " (jf/org-macro-value-list "em"))) ")}}}") 85 | (macro-mechanic "{{{m(" (p (completing-read "Mechanic: " (jf/org-macro-value-list "m(?:echanic)?"))) ")}}}") 86 | (errant-monster "** " (p "Monster Name") n 87 | ":PROPERTIES:" n 88 | ":ID: " (org-id-new) n 89 | ":SOURCE:" p n 90 | ":THREAT: " p n 91 | ":HP: " p n 92 | ":ATTACKS: " p n 93 | ":MD: " p n 94 | ":MORALE: " p n 95 | ":ALIGNMENT: " p n 96 | ":END:" n q) 97 | 98 | (update_block "#+attr_shortcode: :date " (format-time-string "%Y-%m-%d") 99 | " :mode " (p (completing-read "Mode: " '(inline paragraph marginnote sidenote section fallback) nil t) mode) 100 | n "#+begin_update" 101 | n q 102 | n "#+end_update" n) 103 | 104 | (verb_block 105 | (p (completing-read "Method: " '("get" "post" "put") nil t) method t) 106 | (p (read-string "URL: ") url t) 107 | (p (completing-read "Content-Type: " '("text/html" "text/plain" "application/json" "application/xml") nil t) type t) 108 | "#+begin_src verb :wrap src ob-verb-response" n 109 | > method " " url n 110 | > "Content-Type: " type "; charset=utf-8" n 111 | > q 112 | "#+end_src") 113 | 114 | ;;;; text-mode 115 | text-mode 116 | 117 | ;; This is an example of using the value of `(s str)' to populate the above and 118 | ;; below line! 119 | (asciibox "+-" (make-string (length str) ?-) "-+" n 120 | "| " (s str) " |" n 121 | "+-" (make-string (length str) ?-) "-+" n) 122 | 123 | ;; (banner comment-start (make-string (length title) ?=) n comment-start (p "Title: " title) n comment-start (make-string (length title) ?=)) 124 | 125 | go-ts-mode go-mode 126 | (regex "rx := regexp.MustCompile(`" p "`)") 127 | (db "runtime.Breakpoint()" n) 128 | (tnew "package " p "_test" n n "import(" n " \"testing\"" n " \"github.com/stretchr/testify/assert\"" n ")" n n "func Test" p "(t *testing.T) {" n q n " assert.Nil(t, nil)" n "}") 129 | (thelper "func helper" p "(t *testing.T) {" n > "t.Helper()" n > q n "}") 130 | (tfunc "func Test" p "(t *testing.T) {" n > q n "}") 131 | (trun "t.Run(\"" p "\", func(t *testing.T) {" n > q n "})") 132 | (ttable "test_cases := struct {" n > p n "}{" n > "{" p "}" n "}" n n "for _, test_case := range test_cases {" n > q n "}") 133 | (options "type " (p "Options for: " optInterface) "Option interface {" 134 | n> "apply(*" optInterface ")" n 135 | "}" 136 | n n"type " (string-inflection-camelcase-function optInterface) "Option_" (p "Option Name: " withOpt) " struct {}" 137 | n n "func (o " (string-inflection-camelcase-function optInterface) "Option_" withOpt ") apply(i *" optInterface ") {" 138 | n> "// Make it so!" 139 | n> q 140 | n "}" 141 | n n "func " optInterface "Option_" withOpt "() " optInterface "Option {" 142 | n> "return " (string-inflection-camelcase-function optInterface) "Option_" withOpt "{}" 143 | n "}") 144 | 145 | ;;;; web-mode 146 | web-mode 147 | (bb "<% byebug %>" n) 148 | (db "<% require \"debug\"; binding.break %>" n) 149 | (tor "Take on Rules") 150 | (today "") 151 | 152 | ;;; Multi-Mode Entries 153 | 154 | ;;;; enh-ruby-mode ruby-mode 155 | enh-ruby-mode ruby-mode rspec-mode ruby-ts-mode 156 | 157 | (bench "require \"benchmark\"" n "Benchmark.bmbm do |b|" n " b.report(\"\") do" n " # Stuff" n " end" n "end") 158 | (cattr > "##" n> 159 | "# @!group Class Attributes" n> 160 | "#" n> 161 | "# @!attribute " (s attr) n> 162 | "# " p n> 163 | "class_attribute :" attr ", " q n> 164 | "# @!endgroup Class Attributes" n> 165 | "##") 166 | 167 | (db (jf/require-debugger) n) 168 | (frozen "# frozen_string_literal: true") 169 | (shell "#!/usr/bin/env ruby -w") 170 | (dc "described_class") 171 | (rspec "# frozen_string_literal: true" n n "require 'spec_helper'" n n "RSpec.describe " q " do" n n "end") 172 | (subject "subject(:" p ") { " p " }") 173 | (struct p " = Struct.new(" p ", keyword_init: true)") 174 | ;; A little experiment in mixing tempel behavior and multiple values. 175 | (init (p "Parameters (comma separated): " given-params t) 176 | (let* ((params (mapcar #'s-trim (s-split "," given-params))) 177 | (kwargs (mapcar (lambda (el) (concat el ":")) params)) 178 | (symbols (mapcar (lambda (el) (concat ":" el)) params))) 179 | (dolist (param params) (insert "# @param " param " [Object]\n")) 180 | (insert "def initialize(" (s-join ", " kwargs) ")\n") 181 | (dolist (param params) (insert " @" param " = " param "\n")) 182 | (insert "end\n\n" 183 | "attr_reader " (s-join ", " symbols) "\n"))) 184 | 185 | (it_is "it { is_expected.to " q " }") 186 | -------------------------------------------------------------------------------- /treesit.rb: -------------------------------------------------------------------------------- 1 | module A; end 2 | module A::B 3 | module C; end 4 | C::D = Struct.new(:hello) do 5 | def call; end 6 | end 7 | end 8 | 9 | # @param the [Object] lorem ipsum dolum you can use them to create new tools is truly you can use to 10 | # lorem ipsum dolum you can use them to create new tools is truly you can use to 11 | # 12 | # @example 13 | # 14 | # Hello World 15 | # 16 | # @param the [Object] lorem ipsum dolum you can use them to create new tools is truly you can use to 17 | # create new tools is truly you can use them to create new tools is truly lorem ipsum dolum 18 | # you can use them to create new tools is truly you can use them to create new tools is truly 19 | # you can use them to create new tools is truly hello world 20 | # 21 | # @see #failure you can use them to create new tools is truly you can use them to create new tools 22 | # is truly 23 | # 24 | # @param key [Object] 25 | # @param kwargs [Object] 26 | # @param args [Object] 27 | # @param block [Object] 28 | # 29 | # @return [Class] lorem ipsum dolum you can use them to create new tools is truly you can use them 30 | # to create new tools is truly you can use them to create new tools is truly lorem ipsum 31 | # dolum you can use them to create new tools is truly you can use them to create new tools 32 | # is truly you can use them to create new tools is truly 33 | def hello(the, world:, key: :value, **kwargs, *args, &block) 34 | end 35 | 36 | # @return [Class] lorem ipsum dolum you can use them to create new tools is truly you can use them 37 | # to create new tools is truly you can use them to create new tools is truly lorem ipsum 38 | # dolum you can use them to create new tools is truly you can use them to create new tools 39 | # is truly you can use them to create new tools is truly 40 | --------------------------------------------------------------------------------