From 8081ade1b7e6e4afa33c570cd7e608995551dc73 Mon Sep 17 00:00:00 2001 From: Musa Al-hassy Date: Tue, 26 Sep 2023 16:55:34 -0400 Subject: [PATCH] Lots of cleaning --- .aspell.en.pws | 14 +- .github/workflows/main.yml | 2 +- init-test.el | 24 +- init.el | 2172 +++- init.org | 20665 ++++++++++++++++++----------------- yankpad.org | 583 +- 6 files changed, 12402 insertions(+), 11058 deletions(-) diff --git a/.aspell.en.pws b/.aspell.en.pws index cffe58b..b8d46de 100644 --- a/.aspell.en.pws +++ b/.aspell.en.pws @@ -1,4 +1,4 @@ -personal_ws-1.1 en 324 +personal_ws-1.1 en 336 ACSII AST ASTs @@ -23,6 +23,7 @@ Discovarability Distributivity Dualise Dually +Eglot Equipollence Excerption Executables @@ -54,6 +55,8 @@ Monoid Monoids Monotonicity Muawiyah +Multimethod +Multimethods Musa Nasriyah Neato @@ -61,9 +64,11 @@ Netmask Orchestrator PackageFormer PackageFormer's +Parametricity Prepend Procedurally Prolog +Protobufs Pseudocode Pushout Quran @@ -76,6 +81,8 @@ Sessional Shyam Subsegment Surjection +TODO +Tcl TypeScript Typecheck Unbundling @@ -129,6 +136,7 @@ compositional computability computable connectedness +continous contrapositive datatypes deallocate @@ -142,6 +150,7 @@ decrypting definitional definitionaly deliverables +dereference dereferencing destructor destructure @@ -187,6 +196,7 @@ indiscernibles indistinguishability infimum init +initializers injective inlined inlining @@ -261,6 +271,7 @@ pushout pushouts quantifications reachability +rebased redistributable reduct representable @@ -278,6 +289,7 @@ serialize sessional siloed stepwise +stringify subalgebras subclasses subexpression diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 67afe13..66d821f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -# [[file:../../init.org::#Github-Actions][Github Actions:1]] +# [[file:../../init.org::*Github Actions][Github Actions:1]] # This workflow will do a clean install of dependencies and run tests # For more information see: https://help.github.com/actions/language-and-framework-guides/ diff --git a/init-test.el b/init-test.el index 2f4ee4c..39c3981 100644 --- a/init-test.el +++ b/init-test.el @@ -1,4 +1,4 @@ -;; [[file:init.org::#Word-Completion][Word Completion:2]] +;; [[file:init.org::*Word Completion and Documentation Pop-ups][Word Completion and Documentation Pop-ups:2]] (ert-deftest company-works-as-expected-in-text-mode () :tags '(company) (switch-to-buffer "*TESTING COMPANY MODE ~ Text*") @@ -50,7 +50,7 @@ (should (looking-back "finally")) (kill-buffer)) -;; Word Completion:2 ends here +;; Word Completion and Documentation Pop-ups:2 ends here ;; [[file:init.org::*E2E Test][E2E Test:1]] (ert-deftest hideshow-is-enabled-and-folds-by-default () @@ -76,23 +76,3 @@ (kill-buffer))) ;; E2E Test:1 ends here - -;; [[file:init.org::#LSP-Making-Emacs-into-a-generic-full-featured-programming-IDE][LSP: Making Emacs into a generic full-featured programming IDE:6]] -(ert-deftest lsp-hover-shows-type-signature () - ;; Make a temporary scratch.js file with the given contents. - (-let [scratch.js (make-temp-file "scratch" nil ".js" "const first = (x, y) => 3")] - (find-file scratch.js) - (lsp-workspace-folders-add (f-parent scratch.js)) - (lsp) - - ;; lsp-hover uses lsp--eldoc-message, so let's save the hover info. - (advice-add #'lsp--eldoc-message :before (lambda (&rest msg) (setq my/lsp-hover-message (substring-no-properties (car msg))))) - - (end-of-buffer) - (insert "\n first") - (lsp-hover) ;; Alternatively: (lsp-describe-thing-at-point) - (should (equal "const first: (x: any, y: any) => number" my/lsp-hover-message)) - - (save-buffer) - (kill-buffer))) -;; LSP: Making Emacs into a generic full-featured programming IDE:6 ends here diff --git a/init.el b/init.el index b5d5b0a..65fe730 100644 --- a/init.el +++ b/init.el @@ -1,11 +1,13 @@ -;; [[file:init.org::+begin_src emacs-lisp][No heading:1]] -(require 'cl-lib) +;; [[file:init.org::*Personal instructions for a new machine][Personal instructions for a new machine:4]] + (setq org-image-actual-width nil) +;; Personal instructions for a new machine:4 ends here - (cl-defun maybe-clone (remote &optional local)) +;; [[file:init.org::*Personal instructions for a new machine][Personal instructions for a new machine:5]] + ;; Clicking on a URL, or running M-x browse-url, should open the URL *within* Emacs. + (setq browse-url-browser-function #'xwidget-webkit-browse-url) - ;; Prevent undo tree files from polluting your git repo - (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))) -;; No heading:1 ends here + ;; (use-package xwwp) ;; Enhance the Emacs xwidget-webkit browser +;; Personal instructions for a new machine:5 ends here (setq custom-file "~/.emacs.d/custom.el") (ignore-errors (load custom-file)) ;; It may not yet exist. @@ -49,7 +51,7 @@ (setq which-key-idle-delay 0.05)) ;; Haskell's cool -(use-package haskell-mode :defer t) +(use-package haskell-mode ) ;; Lisp libraries with Haskell-like naming. (use-package dash) ;; “A modern list library for Emacs” @@ -71,23 +73,26 @@ ;; e.g., f-delete, f-mkdir, f-move, f-exists?, f-hidden? (use-package f) -;; Allow tree-semantics for undo operations. -(use-package undo-tree - :defer nil - :bind ("C-x u" . undo-tree-visualize) - :hook (org-mode . undo-tree-mode) ;; For some reason, I need this. FIXME. - :config - ;; Always have it on - (global-undo-tree-mode) - - ;; Each node in the undo tree should have a timestamp. - (setq undo-tree-visualizer-timestamps t) - - ;; Show a diff window displaying changes between undo nodes. - (setq undo-tree-visualizer-diff t)) - -;; Execute (undo-tree-visualize) then navigate along the tree to witness -;; changes being made to your file live! + ;; Allow tree-semantics for undo operations. + (use-package undo-tree + :defer nil + :bind ("C-x u" . undo-tree-visualize) + :hook (org-mode . undo-tree-mode) ;; For some reason, I need this. FIXME. + :config + ;; Always have it on + (global-undo-tree-mode) + + ;; Each node in the undo tree should have a timestamp. + (setq undo-tree-visualizer-timestamps t) + + ;; Show a diff window displaying changes between undo nodes. + (setq undo-tree-visualizer-diff t) + + ;; Prevent undo tree files from polluting your git repo + (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))) + + ;; Execute (undo-tree-visualize) then navigate along the tree to witness + ;; changes being made to your file live! (use-package quelpa :custom (quelpa-upgrade-p t "Always try to update packages") @@ -100,9 +105,10 @@ (require 'quelpa-use-package)) ;; Auto installing OS system packages - (use-package use-package-ensure-system-package - :config (system-packages-update)) +(use-package system-packages :config (system-packages-update)) +;; Install OS packages using `use-package`. +(use-package use-package-ensure-system-package) ;; Caching the installed pkgs list makes system-package-ensure return nearly immediately for things already installed! (setq my/installed-packages (shell-command-to-string "brew list")) @@ -253,6 +259,9 @@ installs of pacakges that are not in our `my/installed-packages' listing. ;; :custom (helm-icons-provider 'all-the-icons) ;; :config (helm-icons-enable)) +;; When I want to see the TOC of an Org file, show me down to 3 subheadings. +(setq org-imenu-depth 3) + (setq helm-mini-default-sources '(helm-source-buffers-list helm-source-recentf helm-source-bookmarks @@ -354,11 +363,139 @@ installs of pacakges that are not in our `my/installed-packages' listing. ;; Let's execute Lisp code with links, as in “elisp:view-hello-file”. (setq org-confirm-elisp-link-function nil) -(cl-defun all-the-icons-faicon (icon &rest _) - #("" 0 1 (rear-nonsticky t display (raise -0.24) font-lock-face (:family "FontAwesome" :height 1.2) face (:family "FontAwesome" :height 1.2)))) - -(use-package all-the-icons - :config (all-the-icons-install-fonts 'install-without-asking)) +;; Invoke all possible key extensions having a common prefix by +;; supplying the prefix only once. +(use-package hydra :defer nil) + +;; Show hydras overlayed in the middle of the frame +(use-package hydra-posframe + :disabled "TODO Fix me, breaking Github Actions test setup" + :quelpa (hydra-posframe :fetcher git :url + "https://github.com/Ladicle/hydra-posframe.git") + :hook (after-init . hydra-posframe-mode) + :custom (hydra-posframe-border-width 5)) + +;; Neato doc strings for hydras +(use-package pretty-hydra :defer nil) + +;; TODO convert my existing defhydras to my/defhydra. +(defmacro my/defhydra (key title icon-name &rest body) +"Make a hydra whose heads appear in a pretty pop-up window. +Heads are signalled by keywords and the hydra has an icon in its title. + +KEY [String]: Global keybinding for the new hydra. + +TITLE [String]: Either a string or a plist, as specified for pretty-hydra-define. + The underlying Lisp function's name is derived from the TITLE; + which is intentional since hydra's are for interactive, pretty, use. + + One uses a plist TITLE to specify what a hydra should do *before* + any options, or to specify an alternate quit key (:q by default). + +ICON-NAME [Symbol]: Possible FontAwesome icon-types: C-h v `all-the-icons-data/fa-icon-alist'. + +BODY: A list of columns and entries. Keywords indicate the title + of a column; 3-lists (triples) indicate an entry key and + the associated operation to perform and, optionally, a name + to be shown in the pop-up. See DEFHYDRA for more details. + + +For instance, the verbose mess: + + ;; Use ijkl to denote ↑←↓→ arrows. + (global-set-key + (kbd \"C-c w\") + (pretty-hydra-define my/hydra/\\t\\tWindow\\ Adjustment + ;; Omitting extra work to get an icon into the title. + (:title \"\t\tWindow Adjustment\" :quit-key \"q\") + (\"Both\" + ((\"b\" balance-windows \"balance\") + (\"s\" switch-window-then-swap-buffer \"swap\")) + \"Vertical adjustment\" + ((\"h\" enlarge-window \"heighten\") + (\"l\" shrink-window \"lower\")) + \"Horizontal adjustment\" + ((\"n\" shrink-window-horizontally \"narrow\") + (\"w\" enlarge-window-horizontally \"widen\" ))))) + +Is replaced by: + + ;; Use ijkl to denote ↑←↓→ arrows. + (my/defhydra \"C-c w\" \"\t\tWindow Adjustment\" windows + :Both + (\"b\" balance-windows \"balance\") + (\"s\" switch-window-then-swap-buffer \"swap\") + :Vertical_adjustment + (\"h\" enlarge-window \"heighten\") + (\"l\" shrink-window \"lower\") + :Horizontal_adjustment + (\"n\" shrink-window-horizontally \"narrow\") + (\"w\" enlarge-window-horizontally \"widen\"))" + (let* ((name (intern (concat "my/hydra/" + (if (stringp title) + title + (plist-get title :title))))) + (icon-face `(:foreground ,(face-background 'highlight))) + (iconised-title + (concat + (when icon-name + (concat + (all-the-icons-faicon (format "%s" icon-name) :face icon-face :height 1.0 :v-adjust -0.1) + " ")) + (propertize title 'face icon-face)))) + `(global-set-key + (kbd ,key) + (pretty-hydra-define ,name + ,(if (stringp title) + (list :title iconised-title + :quit-key "q") + title) + ,(thread-last body + (-partition-by-header #'keywordp) + (--map (cons (s-replace "_" " " (s-chop-prefix ":" (symbol-name (car it)))) (list (cdr it)))) + (-flatten-n 1)))))) + +(my/defhydra "C-n" "\t\t\t\t\tTextual Navigation" arrows + :Line + ("n" next-line) + ("p" previous-line) + ("a" beginning-of-line) + ("e" move-end-of-line) + ("g" goto-line) + :Word + ("f" forward-word "Next") + ("b" backward-word "Previous") + ("{" org-backward-element "Next Element") + ("}" org-forward-element "Previous Element") + :Screen + ("v" scroll-up-command "Scroll Down") + ("V" scroll-down-command "Scroll Up") + ("l" recenter-top-bottom "Center Page") + ("r" move-to-window-line-top-bottom "Relocate Point") + ("m" helm-imenu "Textual Menu")) + +;; C-n, next line, inserts newlines when at the end of the buffer +(setq next-line-add-newlines t) + +;; Use ijkl to denote ↑←↓→ arrows. +(my/defhydra "C-c w" "\t\tWindow Adjustment" windows + :Both + ("b" balance-windows "balance") + ("s" switch-window-then-swap-buffer "swap") + :Vertical_adjustment + ("h" enlarge-window "heighten") + ("l" shrink-window "lower") + :Horizontal_adjustment + ("n" shrink-window-horizontally "narrow") + ("w" enlarge-window-horizontally "widen")) + +;; Provides a *visual* way to choose a window to switch to. +;; (use-package switch-window ) +;; :bind (("C-x o" . switch-window) +;; ("C-x w" . switch-window-then-swap-buffer)) + +;; Have a thick ruler between vertical windows +(window-divider-mode) ;; change all prompts to y or n (fset 'yes-or-no-p 'y-or-n-p) @@ -371,6 +508,329 @@ installs of pacakges that are not in our `my/installed-packages' listing. ;; initially-worrisome “narrow-to-region”, C-x n n. (setq-default disabled-command-function nil) +(use-package vterm) ;; Shell with a nearly universal compatibility with terminal applications 💝 + +;; "Intelligent" switching to vterm; eg creates it if it's not open, non-intrusive windowing, saves window setup, etc. +(use-package vterm-toggle + :bind* ("C-t" . vterm-toggle)) + +;; Be default, Emacs please use zsh +;; E.g., M-x shell +(unless noninteractive (setq shell-file-name "/bin/zsh")) + +(system-packages-ensure "tldr") + +;; [[file:init.org::*Manipulating Sections][Manipulating Sections:1]] +(setq org-use-speed-commands t) +;; Manipulating Sections:1 ends here + +;; [[file:init.org::*Manipulating Sections][Manipulating Sections:2]] +;; When refiling, only show me top level headings [Default]. Sometimes 2 is useful. +;; When I'm refiling my TODOS, then give me all the freedom. +(setq org-refile-targets '((nil :maxlevel . 1) + (org-agenda-files :maxlevel . 9))) + +;; Maybe I want to refile into a new heading; confirm with me. +(setq org-refile-allow-creating-parent-nodes 'confirm) + +;; Use full outline paths for refile targets +;; When refiling, using Helm, show me the hierarchy paths +(setq org-outline-path-complete-in-steps nil) +(setq org-refile-use-outline-path 'file-path) +;; Manipulating Sections:2 ends here + +;; [[file:init.org::*Manipulating Sections][Manipulating Sections:3]] +;; TODO FIXME Crashes upon startup. +(when nil (add-to-list 'org-speed-commands (cons "P" #'org-set-property))) +;; Use ‘:’ and ‘e’ to set tags and effort, respectively. +;; Manipulating Sections:3 ends here + +;; [[file:init.org::*Seamless Navigation Between Source Blocks][Seamless Navigation Between Source Blocks:1]] +;; Overriding keys for printing buffer, duplicating gui frame, and isearch-yank-kill. +;; +(require 'org) +(use-package emacs + :bind (:map org-mode-map + ("s-p" . org-babel-previous-src-block) + ("s-n" . org-babel-next-src-block) + ("s-e" . org-edit-special) + :map org-src-mode-map + ("s-e" . org-edit-src-exit))) +;; Seamless Navigation Between Source Blocks:1 ends here + +;; [[file:init.org::*Modifying \[\[kbd:⟨return⟩\]\]][Modifying [[kbd:⟨return⟩]]:1]] +(add-hook 'org-mode-hook '(lambda () + (local-set-key (kbd "") 'org-return-indent)) + (local-set-key (kbd "C-M-") 'electric-indent-just-newline)) +;; Modifying [[kbd:⟨return⟩]]:1 ends here + +;; [[file:init.org::*Executing code from ~src~ blocks][Executing code from ~src~ blocks:1]] +;; Seamless use of babel: No confirmation upon execution. +;; Downside: Could accidentally evaluate harmful code. +(setq org-confirm-babel-evaluate nil) + +;; Never evaluate code blocks upon export and replace results when evaluation does occur. +;; For a particular language 𝑳, alter ‘org-babel-default-header-args:𝑳’. +(setq org-babel-default-header-args + '((:results . "replace") + (:session . "none") + (:exports . "both") + (:cache . "no") + (:noweb . "no") + (:hlines . "no") + (:tangle . "no") + (:eval . "never-export"))) +;; Executing code from ~src~ blocks:1 ends here + +;; [[file:init.org::*Executing code from ~src~ blocks][Executing code from ~src~ blocks:2]] +(defvar my/programming-languages + '(emacs-lisp shell python haskell + ;; rust ;; FIXME: There's an error wrt ob-rust: Cannot open load file: No such file or directory, ob-rust + ruby ocaml dot latex org js css + sqlite C) ;; Captial “C” gives access to C, C++, D + "List of languages I have used in Org-mode, for literate programming.") + +;; Load all the languagues +;; FIXME: There's an error wrt ob-rust: Cannot open load file: No such file or directory, ob-rust +(ignore-errors (cl-loop for lang in my/programming-languages + do (require (intern (format "ob-%s" lang))))) +;; +(org-babel-do-load-languages + 'org-babel-load-languages + (--map (cons it t) my/programming-languages)) + +;; Preserve my indentation for source code during export. +(setq org-src-preserve-indentation t) + +;; The export process hangs Emacs, let's avoid this. +;; MA: For one reason or another, this crashes more than I'd like. +;; (setq org-export-in-background t) +;; Executing code from ~src~ blocks:2 ends here + +;; [[file:init.org::*Executing all =#+name: startup-code= for local configurations][Executing all =#+name: startup-code= for local configurations:1]] +(defun my/execute-startup-blocks () + "Execute all startup blocks, those named ‘startup-code’. + +I could not use ORG-BABEL-GOTO-NAMED-SRC-BLOCK since it only goes +to the first source block with the given name, whereas I'd like to +visit all blocks with such a name." + (interactive) + (save-excursion + (goto-char 0) + (while (ignore-errors (re-search-forward "^\\#\\+name: startup-code")) + (org-babel-execute-src-block)))) +;; Executing all =#+name: startup-code= for local configurations:1 ends here + +;; [[file:init.org::*Executing all =#+name: startup-code= for local configurations][Executing all =#+name: startup-code= for local configurations:2]] +;; Please ask me on a file by file basis whether its local variables are ‘safe’ +;; or not. Use ‘!’ to mark them as permanently ‘safe’ to avoid being queried +;; again for the same file. +(setq enable-local-variables t) +;; Executing all =#+name: startup-code= for local configurations:2 ends here + +;; [[file:init.org::*Word Completion and Documentation Pop-ups][Word Completion and Documentation Pop-ups:1]] +(use-package company + + :config + (global-company-mode 1) + (setq ;; Only 2 letters required for completion to activate. + company-minimum-prefix-length 2 + + ;; Search other buffers for compleition candidates + company-dabbrev-other-buffers t + company-dabbrev-code-other-buffers t + + ;; Show candidates according to importance, then case, then in-buffer frequency + company-transformers '(company-sort-by-backend-importance + company-sort-prefer-same-case-prefix + company-sort-by-occurrence) + + ;; Flushright any annotations for a compleition; + ;; e.g., the description of what a snippet template word expands into. + company-tooltip-align-annotations t + + ;; Allow (lengthy) numbers to be eligible for completion. + company-complete-number t + + ;; M-⟪num⟫ to select an option according to its number. + company-show-numbers t + + ;; Show 10 items in a tooltip; scrollbar otherwise or C-s ^_^ + company-tooltip-limit 10 + + ;; Edge of the completion list cycles around. + company-selection-wrap-around t + + ;; Do not downcase completions by default. + company-dabbrev-downcase nil + + ;; Even if I write something with the ‘wrong’ case, + ;; provide the ‘correct’ casing. + company-dabbrev-ignore-case nil + + ;; Immediately activate completion. + company-idle-delay 0) + + ;; Use C-/ to manually start company mode at point. C-/ is used by undo-tree. + ;; Override all minor modes that use C-/; bind-key* is discussed below. + (bind-key* "C-/" #'company-manual-begin) + + ;; Bindings when the company list is active. + :bind (:map company-active-map + ("C-d" . company-show-doc-buffer) ;; In new temp buffer + ("" . company-complete-selection) + ;; Use C-n,p for navigation in addition to M-n,p + ("C-n" . (lambda () (interactive) (company-complete-common-or-cycle 1))) + ("C-p" . (lambda () (interactive) (company-complete-common-or-cycle -1))))) + +;; It's so fast that we don't need a key-binding to start it! +;; Word Completion and Documentation Pop-ups:1 ends here + +;; [[file:init.org::*Word Completion and Documentation Pop-ups][Word Completion and Documentation Pop-ups:3]] +(defun my/ensure-machine-works-as-expected () + "Run all my personal tests to ensure Emacs behaves as I expect it to." + (interactive) + (load-file "init-test.el") + (ert t) + (ert-results-pop-to-timings)) +;; Word Completion and Documentation Pop-ups:3 ends here + +;; [[file:init.org::*Word Completion and Documentation Pop-ups][Word Completion and Documentation Pop-ups:4]] +(use-package company-emoji + :config (add-to-list 'company-backends 'company-emoji)) +;; Word Completion and Documentation Pop-ups:4 ends here + +;; [[file:init.org::*Word Completion and Documentation Pop-ups][Word Completion and Documentation Pop-ups:5]] +(use-package emojify + :config (setq emojify-display-style 'image) + :init (global-emojify-mode 1)) ;; Will install missing images, if need be. +;; Word Completion and Documentation Pop-ups:5 ends here + +;; [[file:init.org::*Documentation Pop-Ups][Documentation Pop-Ups:1]] +(use-package company-quickhelp + :config + (setq company-quickhelp-delay 0.1) + (company-quickhelp-mode) + ;; Especially when learning a new language, looking up its definition/docstring can be helpful. + ;; Note: I use “M-!” everywhere else to mean “define word at point”. + ) + +;; TODO: Consider adding a hook to company mode to immediately call (company-quickhelp--show), in prog-modes +;; Documentation Pop-Ups:1 ends here + +;; [[file:init.org::*Intro to Snippets][Intro to Snippets:1]] +;; Add yasnippet support for all company backends +;; +(cl-defun my/company-backend-with-yankpad (backend) + "There can only be one main completition backend, so let's + enable yasnippet/yankpad as a secondary for all completion + backends. + + Src: https://emacs.stackexchange.com/a/10520/10352" + + (if (and (listp backend) (member 'company-yankpad backend)) + backend + (append (if (consp backend) backend (list backend)) + '(:with company-yankpad)))) +;; Intro to Snippets:1 ends here + +;; [[file:init.org::*Intro to Snippets][Intro to Snippets:2]] +;; Yet another snippet extension program +(use-package yasnippet + :config + (yas-global-mode 1) ;; Always have this on for when using yasnippet syntax within yankpad + ;; respect the spacing in my snippet declarations + (setq yas-indent-line 'fixed)) + +;; Alternative, Org-based extension program +(use-package yankpad + + :config + ;; Location of templates + (setq yankpad-file "~/.emacs.d/yankpad.org") + + ;; Ignore major mode, always use defaults. + ;; Yankpad will freeze if no org heading has the name of the given category. + (setq yankpad-category "Default") + + ;; Load the snippet templates ---useful after yankpad is altered + (yankpad-reload) + + ;; Set company-backend as a secondary completion backend to all existing backends. + (setq company-backends (mapcar #'my/company-backend-with-yankpad company-backends))) +;; Intro to Snippets:2 ends here + +;; [[file:init.org::*Intro to Snippets][Intro to Snippets:5]] +(cl-defun org-insert-link () + "Makes an org link by inserting the URL copied to clipboard and + prompting for the link description only. + + Type over the shown link to change it, or tab to move to the + description field. + + This overrides Org-mode's built-in ‘org-insert-link’ utility; + whence C-c C-l uses the snippet." + (interactive) + (insert "my_org_insert_link") + (yankpad-expand)) +;; Intro to Snippets:5 ends here + +;; [[file:init.org::*Emojis][Emojis:2]] +;; Get all unicode emojis to appear within Emacs +;; See also: https://emacs.stackexchange.com/questions/5689/force-a-single-font-for-all-unicode-glyphs?rq=1 +(unless noninteractive (set-fontset-font t nil "Apple Color Emoji")) +;; Emojis:2 ends here + +;; [[file:init.org::*Prettify inline source code][Prettify inline source code:1]] +;; Show “ src_emacs-lisp[:exports results]{ 𝒳 } ” as “ ℰ𝓁𝒾𝓈𝓅﴾ 𝒳 ﴿ ”. +;; +(font-lock-add-keywords 'org-mode + '(("\\(src_emacs-lisp\\[.*]{\\)\\([^}]*\\)\\(}\\)" + (1 '(face (:inherit (bold) :foreground "gray65") display "ℰ𝓁𝒾𝓈𝓅﴾")) + (2 '(face (:foreground "blue"))) + (3 '(face (:inherit (bold) :foreground "gray65") display "﴿")) + ))) +;; +;; Let's do this for all my languages: +;; Show “ src_LANGUAGE[…]{ ⋯ } ” as “ ﴾ ⋯ ﴿ ”. +(cl-loop for lang in my/programming-languages + do (font-lock-add-keywords 'org-mode + `(( ,(format "\\(src_%s\\[.*]{\\)\\([^}]*\\)\\(}\\)" lang) + (1 '(face (:inherit (bold) :foreground "gray65") display "﴾")) + (2 '(face (:foreground "blue"))) + (3 '(face (:inherit (bold) :foreground "gray65") display "﴿")) + )))) + +;; +(defun my/toggle-line-fontification () + "Toggle the fontification of the current line" + (interactive) + (defvar my/toggle-fontify/current-line -1) + (defvar my/toggle-fontify/on? nil) + (add-to-list 'font-lock-extra-managed-props 'display) + (let ((start (line-beginning-position)) (end (line-end-position))) + (cond + ;; Are we toggling the current line? + ((= (line-number-at-pos) my/toggle-fontify/current-line) + (if my/toggle-fontify/on? + (font-lock-fontify-region start end) + (font-lock-unfontify-region start end)) + (setq my/toggle-fontify/on? (not my/toggle-fontify/on?))) + ;; Nope, we've moved on to another line. + (:otherwise + (setq my/toggle-fontify/current-line (line-number-at-pos) + my/toggle-fontify/on? :yes_please_fontify) + (font-lock-unfontify-region start end))))) + + ;; TODO FIXME; maybe ignore: Wasted too much time here already. +;; (add-hook 'post-command-hook #'my/toggle-line-fontification nil t) +;; (font-lock-add-keywords nil '((my/toggle-line-fontification)) t) +;; Prettify inline source code:1 ends here + +;; [[file:init.org::*Unfold Org Headings when I perform a search][Unfold Org Headings when I perform a search:1]] +(setq org-fold-core-style 'overlays) +;; Unfold Org Headings when I perform a search:1 ends here + ;; [[file:init.org::*Undo-tree: Very Local Version Control][Undo-tree: Very Local Version Control:2]] ;; By default C-z is suspend-frame, i.e., minimise, which I seldom use. (global-set-key (kbd "C-z") @@ -442,22 +902,25 @@ installs of pacakges that are not in our `my/installed-packages' listing. ;; Copy/paste this: https://github.com/dandavison/delta#get-started ;; Intro:2 ends here -;; [[file:init.org::*COMMENTCredentials: I am who I am][COMMENTCredentials: I am who I am:1]] +;; [[file:init.org::*Credentials: I am who I am][Credentials: I am who I am:1]] +;; Only set these creds up if there is no Git email set up ---ie at work I have an email set up, so don't +;; override it with my personal creds. +;; ;; See here for a short & useful tutorial: ;; https://alvinalexander.com/git/git-show-change-username-email-address (when (equal "" (shell-command-to-string "git config user.email ")) (shell-command (format "git config --global user.name \"%s\"" user-full-name)) (shell-command (format "git config --global user.email \"%s\"" user-mail-address))) +;; Credentials: I am who I am:1 ends here -;; Also need to customise email routes per organization -;; https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#customizing-email-routes-per-organization -(ignore-error (unless my/personal-machine? - (shell-command (format "git config --global user.email \"%s\"" work/email)))) -;; COMMENTCredentials: I am who I am:1 ends here +;; [[file:init.org::*Credentials: I am who I am][Credentials: I am who I am:2]] +;; We want to reuse an existing Emacs process from the command line +;; E.g., emacsclient --eval '(+ 1 2)' # ⇒ 3 +(server-start) -;; [[file:init.org::*COMMENTCredentials: I am who I am][COMMENTCredentials: I am who I am:2]] +;; Or use it whenever we are editing a git message from the terminal (shell-command "git config --global core.editor 'emacsclient -t -a=\\\"\\\"'") -;; COMMENTCredentials: I am who I am:2 ends here +;; Credentials: I am who I am:2 ends here ;; [[file:init.org::*Encouraging useful commit messages][Encouraging useful commit messages:1]] (defun my/git-commit-reminder () @@ -542,7 +1005,7 @@ if REMOTE is https://github.com/X/Y then LOCAL becomes ∼/Y." ;; Maybe clone ... everything?:2 ends here ;; [[file:init.org::*Gotta love that time machine][Gotta love that time machine:1]] -(use-package git-timemachine :defer t) +(use-package git-timemachine ) ;; Gotta love that time machine:1 ends here ;; [[file:init.org::*Jump to a (ma)git repository with ~C-u C-x g~][Jump to a (ma)git repository with ~C-u C-x g~:1]] @@ -653,234 +1116,54 @@ if REMOTE is https://github.com/X/Y then LOCAL becomes ∼/Y." ;; [[file:init.org::*Highlighting TODO-s & Showing them in Magit][Highlighting TODO-s & Showing them in Magit:1]] ;; NOTE that the highlighting works even in comments. -(use-package hl-todo - ;; I want todo-words highlighted in prose, not just in code fragements. - :hook (org-mode . hl-todo-mode) - :config - ;; Adding new keywords - (cl-loop for kw in '("TEST" "MA" "WK" "JC") - do (add-to-list 'hl-todo-keyword-faces (cons kw "#dc8cc3"))) - ;; Enable it everywhere. - (global-hl-todo-mode)) -;; Highlighting TODO-s & Showing them in Magit:1 ends here - -;; [[file:init.org::*Highlighting TODO-s & Showing them in Magit][Highlighting TODO-s & Showing them in Magit:3]] -(defun add-watchwords () "Add TODO: words to font-lock keywords." - (font-lock-add-keywords nil - '(("\\(\\ jump to first and final semantic units. -;; If pressed twice, they go to physical first and last positions. -(use-package beginend - :config (beginend-global-mode)) -;; Jumping to extreme semantic units:1 ends here - -;; [[file:init.org::*Manipulating Sections][Manipulating Sections:1]] -(setq org-use-speed-commands t) -;; Manipulating Sections:1 ends here - -;; [[file:init.org::*Manipulating Sections][Manipulating Sections:2]] -;; When refiling, only show me top level headings [Default]. Sometimes 2 is useful. -;; When I'm refiling my TODOS, then give me all the freedom. -(setq org-refile-targets '((nil :maxlevel . 1) - (org-agenda-files :maxlevel . 9))) - -;; Maybe I want to refile into a new heading; confirm with me. -(setq org-refile-allow-creating-parent-nodes 'confirm) - -;; Use full outline paths for refile targets -;; When refiling, using Helm, show me the hierarchy paths -(setq org-outline-path-complete-in-steps nil) -(setq org-refile-use-outline-path 'file-path) -;; Manipulating Sections:2 ends here - -;; [[file:init.org::*Manipulating Sections][Manipulating Sections:3]] -;; TODO FIXME Crashes upon startup. -(when nil (add-to-list 'org-speed-commands (cons "P" #'org-set-property))) -;; Use ‘:’ and ‘e’ to set tags and effort, respectively. -;; Manipulating Sections:3 ends here - -;; [[file:init.org::*Seamless Navigation Between Source Blocks][Seamless Navigation Between Source Blocks:1]] -;; Overriding keys for printing buffer, duplicating gui frame, and isearch-yank-kill. -;; -(require 'org) -(use-package emacs - :bind (:map org-mode-map - ("s-p" . org-babel-previous-src-block) - ("s-n" . org-babel-next-src-block) - ("s-e" . org-edit-special) - :map org-src-mode-map - ("s-e" . org-edit-src-exit))) -;; Seamless Navigation Between Source Blocks:1 ends here - -;; [[file:init.org::*Modifying \[\[kbd:⟨return⟩\]\]][Modifying [[kbd:⟨return⟩]]:1]] -(add-hook 'org-mode-hook '(lambda () - (local-set-key (kbd "") 'org-return-indent)) - (local-set-key (kbd "C-M-") 'electric-indent-just-newline)) -;; Modifying [[kbd:⟨return⟩]]:1 ends here - -;; [[file:init.org::*Executing code from ~src~ blocks][Executing code from ~src~ blocks:1]] -;; Seamless use of babel: No confirmation upon execution. -;; Downside: Could accidentally evaluate harmful code. -(setq org-confirm-babel-evaluate nil) - -;; Never evaluate code blocks upon export and replace results when evaluation does occur. -;; For a particular language 𝑳, alter ‘org-babel-default-header-args:𝑳’. -(setq org-babel-default-header-args - '((:results . "replace") - (:session . "none") - (:exports . "both") - (:cache . "no") - (:noweb . "no") - (:hlines . "no") - (:tangle . "no") - (:eval . "never-export"))) -;; Executing code from ~src~ blocks:1 ends here - -;; [[file:init.org::*Executing code from ~src~ blocks][Executing code from ~src~ blocks:2]] -(defvar my/programming-languages - '(emacs-lisp shell python haskell - ;; rust ;; FIXME: There's an error wrt ob-rust: Cannot open load file: No such file or directory, ob-rust - ruby ocaml dot latex org js css - sqlite C) ;; Captial “C” gives access to C, C++, D - "List of languages I have used in Org-mode, for literate programming.") - -;; Load all the languagues -;; FIXME: There's an error wrt ob-rust: Cannot open load file: No such file or directory, ob-rust -(ignore-errors (cl-loop for lang in my/programming-languages - do (require (intern (format "ob-%s" lang))))) -;; -(org-babel-do-load-languages - 'org-babel-load-languages - (--map (cons it t) my/programming-languages)) - -;; Preserve my indentation for source code during export. -(setq org-src-preserve-indentation t) - -;; The export process hangs Emacs, let's avoid this. -;; MA: For one reason or another, this crashes more than I'd like. -;; (setq org-export-in-background t) -;; Executing code from ~src~ blocks:2 ends here - -;; [[file:init.org::*Executing all =#+name: startup-code= for local configurations][Executing all =#+name: startup-code= for local configurations:1]] -(defun my/execute-startup-blocks () - "Execute all startup blocks, those named ‘startup-code’. - -I could not use ORG-BABEL-GOTO-NAMED-SRC-BLOCK since it only goes -to the first source block with the given name, whereas I'd like to -visit all blocks with such a name." - (interactive) - (save-excursion - (goto-char 0) - (while (ignore-errors (re-search-forward "^\\#\\+name: startup-code")) - (org-babel-execute-src-block)))) -;; Executing all =#+name: startup-code= for local configurations:1 ends here - -;; [[file:init.org::*Executing all =#+name: startup-code= for local configurations][Executing all =#+name: startup-code= for local configurations:2]] -;; Please ask me on a file by file basis whether its local variables are ‘safe’ -;; or not. Use ‘!’ to mark them as permanently ‘safe’ to avoid being queried -;; again for the same file. -(setq enable-local-variables t) -;; Executing all =#+name: startup-code= for local configurations:2 ends here - -;; [[file:init.org::*Quickly pop-up a terminal, run a command, close it ---and zsh][Quickly pop-up a terminal, run a command, close it ---and zsh:1]] -(use-package shell-pop - :custom - ;; This binding toggles popping up a shell, or moving cursour to the shell pop-up. - (shell-pop-universal-key "C-t") - - ;; Percentage for shell-buffer window size. - (shell-pop-window-size 30) - - ;; Position of the popped buffer: top, bottom, left, right, full. - (shell-pop-window-position "bottom") - - ;; Please use an awesome shell. - (shell-pop-term-shell "/bin/zsh")) -;; Quickly pop-up a terminal, run a command, close it ---and zsh:1 ends here +(use-package hl-todo + ;; I want todo-words highlighted in prose, not just in code fragements. + :hook (org-mode . hl-todo-mode) + :config + ;; Adding new keywords + (cl-loop for kw in '("TEST" "MA" "WK" "JC") + do (add-to-list 'hl-todo-keyword-faces (cons kw "#dc8cc3"))) + ;; Enable it everywhere. + (global-hl-todo-mode)) +;; Highlighting TODO-s & Showing them in Magit:1 ends here -;; [[file:init.org::*Quickly pop-up a terminal, run a command, close it ---and zsh][Quickly pop-up a terminal, run a command, close it ---and zsh:2]] -;; Be default, Emacs please use zsh -;; E.g., M-x shell -(unless noninteractive (setq shell-file-name "/bin/zsh")) -;; Quickly pop-up a terminal, run a command, close it ---and zsh:2 ends here +;; [[file:init.org::*Highlighting TODO-s & Showing them in Magit][Highlighting TODO-s & Showing them in Magit:3]] +(defun add-watchwords () "Add TODO: words to font-lock keywords." + (font-lock-add-keywords nil + '(("\\(\\ jump to first and final semantic units. +;; If pressed twice, they go to physical first and last positions. +(use-package beginend + :config (beginend-global-mode)) +;; Jumping to extreme semantic units:1 ends here ;; Get org-headers to look pretty! E.g., * → ⊙, ** ↦ ◯, *** ↦ ★ ;; https://github.com/emacsorphanage/org-bullets @@ -904,7 +1187,22 @@ visit all blocks with such a name." ;; Keep self motivated! (setq frame-title-format '("" "%b - Living The Dream (•̀ᴗ•́)و")) -;; This package requires the fonts included with all-the-icons to be installed. Run M-x all-the-icons-install-fonts to do so. +;; If work machine, then show notes; otherwise show my todos & init side-by-side. +(unless noninteractive + ;; Only run the following when we're in GUI mode; + ;; i.e., don't run it in Github Actions when testing. + (if (not my/personal-machine?) + (find-file "/Users/musa/Google Drive/My Drive/notes.org")) ;; Org-journal for work + (find-file "~/Dropbox/todo.org") + ;; After startup, if Emacs is idle for 10 seconds, then open my work file; + ;; which is a GPG file and so requires passphrase before other things can load. + ;; (run-with-idle-timer 10 nil (lambda () (find-file "~/Desktop/work.org.gpg"))) + (split-window-right) ;; C-x 3 + (other-window 1) ;; C-x 0 + (let ((enable-local-variables :all) ;; Load *all* locals. + (org-confirm-babel-evaluate nil)) ;; Eval *all* blocks. + (ignore-errors (find-file "~/.emacs.d/init.org")))) + ;; The modeline looks really nice with doom-themes, e.g., doom-solarised-light. (use-package doom-modeline :defer nil @@ -931,14 +1229,14 @@ visit all blocks with such a name." ;; (set-face-attribute 'mode-line nil :family it :height 100) ;; (set-face-attribute 'mode-line-inactive nil :family it :height 100)) -(setq doom-modeline-minor-modes t) -(use-package minions - :defer nil - :init (minions-mode)) + ;; A quick hacky way to add stuff to doom-modeline is to add to the mode-line-process list. + ;; E.g.: (add-to-list 'mode-line-process '(:eval (format "%s" (count-words (point-min) (point-max))))) + ;; We likely want to add this locally, to hooks on major modes. + + (setq doom-modeline-minor-modes t) + (use-package minions -;; A quick hacky way to add stuff to doom-modeline is to add to the mode-line-process list. -;; E.g.: (add-to-list 'mode-line-process '(:eval (format "%s" (count-words (point-min) (point-max))))) -;; We likely want to add this locally, to hooks on major modes. + :init (minions-mode 1)) ;; If not for doom-modeline, we'd need to use fancy-battery-mode.el. (display-battery-mode +1) @@ -959,17 +1257,27 @@ visit all blocks with such a name." (setq display-time-default-load-average nil) (setq display-time-load-average nil) +;; ;; Do not show me line numbers, nor column numbers, in the modeline +(column-number-mode -1) +(line-number-mode -1) + +;; Likewise, no need to show me “Top∣Mid∣Bot” in the modeline. +(setq mode-line-percent-position nil) + +;; (setq display-line-numbers-width-start t) +;; (global-display-line-numbers-mode t) + ;; Treat all themes as safe; no query before use. (setf custom-safe-themes t) ;; Nice looking themes ^_^ -(use-package solarized-theme :defer t) -(use-package doom-themes :defer t) +(use-package solarized-theme ) +(use-package doom-themes ) (use-package spacemacs-common - :defer t + :ensure spacemacs-theme) -(use-package stimmung-themes :defer t) -(use-package shanty-themes :defer t) +(use-package stimmung-themes ) +(use-package shanty-themes ) ;; Infinite list of my commonly used themes. (setq my/themes '(doom-laserwave shanty-themes-light stimmung-themes-light stimmung-themes-dark doom-solarized-light doom-vibrant spacemacs-light solarized-gruvbox-dark solarized-gruvbox-light)) @@ -1000,11 +1308,11 @@ themes (•̀ᴗ•́)و" ;; (my/toggle-theme) (my/toggle-theme 'doom-laserwave) -(unless my/work-machine? +(unless nil ;; my/work-machine? ;; Infinite list of my commonly used fonts (setq my/fonts - '("Roboto Mono Light 14" ;; Sleek + '(;; NOPE: Breaks Gerrit! "Roboto Mono Light 14" ;; Sleek "Input Mono 14" "Source Code Pro Light 14" ;; thin, similar to Inconsolata Light "Papyrus 14" @@ -1022,7 +1330,7 @@ themes (•̀ᴗ•́)و" (shell-command "brew tap homebrew/cask-fonts") (system-packages-ensure "svn") ;; Required for the following font installs - (system-packages-ensure "font-roboto-mono") + ;; No thanks! (system-packages-ensure "font-roboto-mono") ;; Makes Gerrit in Chrome look like Gibberish! (system-packages-ensure "font-input") (system-packages-ensure "font-source-code-pro") (system-packages-ensure "font-fira-mono") @@ -1058,15 +1366,62 @@ fonts (•̀ᴗ•́)و" (ignore-errors (my/toggle-font "Source Code Pro Light 14"))) (unless noninteractive - (my/toggle-font "Roboto Mono Light 14") + ;; Breaks Gerrit: (my/toggle-font "Roboto Mono Light 14") (my/toggle-theme 'solarized-gruvbox-light)) +;; Make it very easy to see the line with the cursor. +(global-hl-line-mode t) + +(use-package beacon + :defer nil + :config (setq beacon-color "#666600") + :hook ((org-mode text-mode) . beacon-mode)) + +(use-package dimmer + :defer nil + :config (dimmer-mode)) + +;; (setq visible-bell 1) ;; On MacOS, this shows a caution symbol ^_^ + +;; The doom themes package comes with a function to make the mode line flash on error. +(use-package doom-themes :defer nil) +(require 'doom-themes-ext-visual-bell) +(doom-themes-visual-bell-config) + +(blink-cursor-mode 1) + (unless noninteractive - (tool-bar-mode -1) ;; No large icons please - (scroll-bar-mode -1) ;; No visual indicator please - (menu-bar-mode -1)) ;; The Mac OS top pane has menu options + (tool-bar-mode -1) ;; No large icons please + (scroll-bar-mode -1)) ;; No visual indicator please + ;; (menu-bar-mode -1) ;; The Mac OS top pane has menu options + +(setq show-paren-delay 0) +(setq show-paren-style 'mixed) +(show-paren-mode) + +(use-package rainbow-delimiters + :disabled + :hook ((org-mode prog-mode text-mode) . rainbow-delimiters-mode)) + +(electric-pair-mode 1) + +;; The ‘<’ and ‘>’ are not ‘parenthesis’, so give them no compleition. +(setq electric-pair-inhibit-predicate + (lambda (c) + (or (member c '(?< ?> ?~)) (electric-pair-default-inhibit c)))) + +;; Treat ‘<’ and ‘>’ as if they were words, instead of ‘parenthesis’. +(modify-syntax-entry ?< "w<") +(modify-syntax-entry ?> "w>") + +(set-face-attribute 'org-document-title nil :height 2.0) +;; (set-face-attribute 'org-level-1 nil :height 1.0) +;; Remaining org-level-𝒾 have default height 1.0, for 𝒾 : 1..8. +;; +;; E.g., reset org-level-1 to default. +;; (custom-set-faces '(org-level-1 nil)) -(defvar-local rasmus/org-at-src-begin -1 + (defvar-local rasmus/org-at-src-begin -1 "Variable that holds whether last position was a ") (defvar rasmus/ob-header-symbol ?☰ @@ -1149,17 +1504,19 @@ fonts (•̀ᴗ•́)و" "Musa's personal prettifications.") (cl-loop for pair in '(;; Example of how pairs like this to beautify org block delimiters - ("#+begin_example" . (?ℰ (Br . Bl) ?⇒)) ;; ℰ⇒ - ("#+end_example" . ?⇐) ;; ⇐ - ;; Actuall beautifications - ("==" . ?≈) ("===" . ?≈) ("=" . ?≔) ;; Programming specific prettifications - ("i32" . ?ℤ) ("u32" . ?ℕ) ("f64" . ?ℝ) ;; Rust specific - ("bool" . ?𝔹) - ("fn" . ?λ) - ("<=" . ?≤) (">=" . ?≥) - ("->" . ?→) ("-->". ?⟶) ;; threading operators - ("[ ]" . ?□) ("[X]" . ?☑) ("[-]" . ?◐)) ;; Org checkbox symbols - do (push pair my/prettify-alist)) + ("#+begin_example" . (?ℰ (Br . Bl) ?⇒)) ;; ℰ⇒ + ("#+end_example" . ?⇐) ;; ⇐ + ;; Actuall beautifications + ("==" . ?≈) ("===" . ?≈) ;; ("=" . ?≔) ;; Programming specific prettifications + ("i32" . ?ℤ) ("u32" . ?ℕ) ("f64" . ?ℝ) ;; Rust specific + ("bool" . ?𝔹) + ;; ("\"\"\"\n" . ?“) ("\"\"\"" . ?”) + ("\"\"\"" . ?“) + ("fn" . ?λ) + ("<=" . ?≤) (">=" . ?≥) + ("->" . ?→) ("-->". ?⟶) ;; threading operators + ("[ ]" . ?□) ("[X]" . ?☑) ("[-]" . ?◐)) ;; Org checkbox symbols + do (push pair my/prettify-alist)) ;; Replace all Org [metadata]keywords with the “▷” symbol; e.g., “#+title: Hello” looks like “▷ Hello”. (cl-loop for keyword in '(title author email date description options property startup export_file_name html_head fileimage filetags) @@ -1170,6 +1527,9 @@ fonts (•̀ᴗ•́)و" (setq prettify-symbols-alist (append my/prettify-alist prettify-symbols-alist))))) + +(add-hook 'org-mode-hook (lambda () (push '("# " . (?🎶 (Br . Bl) ?\ )) prettify-symbols-alist))) + ;; Un-disguise a symbol when cursour is inside it or at the right-edge of it. (setq prettify-symbols-unprettify-at-point 'right-edge) @@ -1235,6 +1595,363 @@ fonts (•̀ᴗ•́)و" ;; Use the “#+name” the user provides, instead of generating label identifiers. (setq org-latex-prefer-user-labels t) + (use-package org-sticky-header + :defer nil + :hook (org-mode . org-sticky-header-mode) + :config + (setq-default + org-sticky-header-full-path 'full + ;; Child and parent headings are seperated by a /. + org-sticky-header-outline-path-separator " / ")) + +(use-package persistent-scratch + + ;; In this mode, the usual save key saves to the underlying persistent file. + :bind (:map persistent-scratch-mode-map + ("C-x C-s" . persistent-scratch-save))) + +(defun scratch () + "Recreate the scratch buffer, loading any persistent state." + (interactive) + (switch-to-buffer-other-window (get-buffer-create "*scratch*")) + (condition-case nil (persistent-scratch-restore) (insert initial-scratch-message)) + (org-mode) + (persistent-scratch-mode) + (persistent-scratch-autosave-mode 1)) + +;; This doubles as a quick way to avoid the common formula: C-x b RET *scratch* + +;; Upon startup, close the default scratch buffer and open one as specfied above +(ignore-errors (kill-buffer "*scratch*") (scratch)) + +(setq initial-scratch-message (concat + "#+title: Persistent Scratch Buffer" + "\n#\n# Welcome! This’ a place for trying things out." + "\n#\n# ⟨ ‘C-x C-s’ here saves to ~/.emacs.d/.persistent-scratch ⟩ \n\n")) + + (quelpa '(org-remoteimg :fetcher github :repo "gaoDean/org-remoteimg")) + (require 'org-remoteimg) + (setq url-cache-directory "~/emacs.d/.cache/") + (setq org-display-remote-inline-images 'cache) + +;; Add a note whenever a task's deadline or scheduled date is changed. +(setq org-log-redeadline 'time) +(setq org-log-reschedule 'time) + +(define-key global-map "\C-ca" 'org-agenda) + +;; List of all the files & directories where todo items can be found. Only one +;; for now: My default notes file. +(setq org-agenda-files (list org-default-notes-file)) + +;; Display tags really close to their tasks. +(setq org-agenda-tags-column -10) + +;; How many days ahead the default agenda view should look +(setq org-agenda-span 'day) +;; May be any number; the larger the slower it takes to generate the view. +;; One day is thus the fastest ^_^ + +;; How many days early a deadline item will begin showing up in your agenda list. +(setq org-deadline-warning-days 14) + +;; In the agenda view, days that have no associated tasks will still have a line showing the date. +(setq org-agenda-show-all-dates t) + +;; Scheduled items marked as complete will not show up in your agenda view. +(setq org-agenda-skip-scheduled-if-done t) +(setq org-agenda-skip-deadline-if-done t) + +(setq org-agenda-start-on-weekday nil) + +;; Start each agenda item with ‘○’, then show me it's %timestamp and how many +;; times it's been re-%scheduled. +(setq org-agenda-prefix-format " ○ %t%s") + +(use-package origami ) +(use-package org-super-agenda + + :hook (org-agenda-mode . origami-mode) ;; Easily fold groups via TAB. + :bind (:map org-super-agenda-header-map ("" . origami-toggle-node)) + :config + (org-super-agenda-mode) + (setq org-super-agenda-groups + '((:name "Important" :priority "A") + (:name "Personal" :habit t) + ;; For everything else, nicely display their heading hierarchy list. + (:auto-map (lambda (e) (org-format-outline-path (org-get-outline-path))))))) + +;; MA: No noticable effect when using org-super-agenda :/ +;; +;; Leave new line at the end of an entry. +;; (setq org-blank-before-new-entry '((heading . t) (plain-list-item . t))) + +(setq org-lowest-priority ?C) ;; Now org-speed-eky ‘,’ gives 3 options +(setq org-priority-faces +'((?A :foreground "red" :weight bold) ;; :background "LightCyan1") + (?B :foreground "orange" :weight bold) + (?C :foreground "green" :weight bold))) +;; See all colours with: M-x list-colors-display + +(use-package org-fancy-priorities + + :hook (org-mode . org-fancy-priorities-mode) + :custom (org-fancy-priorities-list '("High" "MID" "LOW")) ;; "OPTIONAL" + ;; Let's use the “Eisenhower map of priority”… + ;; :custom (org-fancy-priorities-list '("Urgent and Important" ;; Do now! + ;; "Not Urgent But Important" ;; Do schedule this. + ;; "Urgent But Not Important" ;; Delegate? + ;; "Not Urgent and Not Important")) ;; Don't do / Optional + ) + +(require 'org-agenda) + +;; How should the columns view look? +(setq org-columns-default-format "%60ITEM(Task) %6Effort(Estim){:} %3PRIORITY %TAGS") + +;; Press “c” in Org agenda to see the columns view; (default binding C-c C-x C-c is too long!) +(org-defkey org-agenda-mode-map "c" #'org-agenda-columns) +(org-defkey org-agenda-mode-map "C" #'org-agenda-goto-calendar) + +;; Press “e” in columns view to alter “e”ffort “e”stimates. +(require 'org-colview) +(org-defkey org-columns-map "e" + ;; Refresh after making an effort estimate. + (lambda () (interactive) (org-agenda-set-effort) (org-agenda-columns))) + + (setq org-tags-column -77) ;; the default + +;; Press ‘:’ on a heading to add a tag, press TAB to see all tags, press RETURN on a tag to add it, press TAB again to add more tags, when all done press RETURN twice. +(use-package helm-org ) ;; Helm for org headlines and keywords completion. +(add-to-list 'helm-completing-read-handlers-alist + '(org-set-tags-command . helm-org-completing-read-tags)) + +;; Also provides: helm-org-capture-templates + +(use-package org-pretty-tags + + :config + (setq org-pretty-tags-surrogate-strings + '(("Neato" . "💡") + ("Blog" . "✍") + ("Audio" . "♬") + ("Video" . "📺") + ("Book" . "📚") + ("Running" . "🏃") + ("Question" . "❓") + ("Wife" . "💕") + ("Text" . "💬") ; 📨 📧 + ("Friends" . "👪") + ("Self" . "🍂") + ("Finances" . "💰") + ("Car" . "🚗") ; 🚙 🚗 🚘 + ("Urgent" . "🔥"))) ;; 📥 📤 📬 + (org-pretty-tags-global-mode 1)) + +;; Tasks get a 25 minute count down timer +(setq org-timer-default-timer 25) + +;; Use the timer we set when clocking in happens. +(add-hook 'org-clock-in-hook + (lambda () (org-timer-set-timer '(16)))) + +;; unless we clocked-out with less than a minute left, +;; show disappointment message. +(add-hook 'org-clock-out-hook + (lambda () + (unless (s-prefix? "0:00" (org-timer-value-string)) + (message-box "The basic 25 minutes on this difficult task are not up; it's a shame to see you leave.")) + (org-timer-stop))) + +(setq org-todo-keywords + '((sequence "TODO(t)" "STARTED(s@/!)" "|" "DONE(d/!)") + (sequence "WAITING(w@/!)" "ON_HOLD(h@/!)" "|" "CANCELLED(c@/!)"))) + +;; Since DONE is a terminal state, it has no exit-action. +;; Let's explicitly indicate time should be noted. +(setq org-log-done 'time) + +(setq org-todo-keyword-faces + '(("TODO" :foreground "red" :weight bold) + ("STARTED" :foreground "blue" :weight bold) + ("DONE" :foreground "forest green" :weight bold) + ("WAITING" :foreground "orange" :weight bold) + ("ON_HOLD" :foreground "magenta" :weight bold) + ("CANCELLED" :foreground "forest green" :weight bold))) + +(setq org-use-fast-todo-selection t) + +;; Install the tool +; (async-shell-command "brew tap adoptopenjdk/openjdk; brew cask install adoptopenjdk13") ;; Dependency +; (async-shell-command "brew install plantuml") + +;; Tell emacs where it is. +;; E.g., (async-shell-command "find / -name plantuml.jar") +(setq org-plantuml-jar-path + "/usr/local/Cellar/plantuml/1.2022.14/libexec/plantuml.jar") + +;; Enable C-c C-c to generate diagrams from plantuml src blocks. +(add-to-list 'org-babel-load-languages '(plantuml . t) ) +(require 'ob-plantuml) + +; Use fundamental mode when editing plantuml blocks with C-c ' +(add-to-list 'org-src-lang-modes '("plantuml" . fundamental)) + +;; Record a note on what was accomplished when clocking out of an item. +(setq org-log-note-clock-out t) + +(setq confirm-kill-emacs 'yes-or-no-p) + +;; Resume clocking task when emacs is restarted +(org-clock-persistence-insinuate) + +;; Show lot of clocking history +(setq org-clock-history-length 23) + +;; Resume clocking task on clock-in if the clock is open +(setq org-clock-in-resume t) + +;; Sometimes I change tasks I'm clocking quickly ---this removes clocked tasks with 0:00 duration +(setq org-clock-out-remove-zero-time-clocks t) + +;; Clock out when moving task to a done state +(setq org-clock-out-when-done t) + +;; Save the running clock and all clock history when exiting Emacs, load it on startup +(setq org-clock-persist t) + +;; Do not prompt to resume an active clock +(setq org-clock-persist-query-resume nil) + +;; Include current clocking task in clock reports +(setq org-clock-report-include-clocking-task t) + + (push '("Effort_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00") + org-global-properties) + +(setq org-clock-sound "~/.emacs.d/school-bell.wav") + +;; Show habits for every day in the agenda. +(setq org-habit-show-habits t) +(setq org-habit-show-habits-only-for-today nil) + +;; This shows the ‘Seinfeld consistency’ graph closer to the habit heading. +(setq org-habit-graph-column 90) + +;; In order to see the habit graphs, which I've placed rightwards, let's +;; always open org-agenda in ‘full screen’. +;; (setq org-agenda-window-setup 'only-window) + +;; Obtain a notifications and text-to-speech utilities +(system-packages-ensure "say") ;; Built-into MacOS, but can be downloaded in Ubuntu +(system-packages-ensure "terminal-notifier") ;; MacOS specific +;; System Preferences → Notifications → Terminal Notifier → Allow “alerts”. +;; E.g.,: (shell-command "terminal-notifier -title \"Hiya\" -message \"hello\"") + +(cl-defun my/notify (message &key (titled "") at repeat-every-hour open) + "Notify user with both an visual banner, with a beep sound, and a text-to-speech recitation. + +When the user clicks on the resulting notification, unless a +given OPEN url is provided, the Emacs application is brough into +focus. + +MESSAGE and TITLE are strings; AT is a time string as expected of +`run-at-time' such as \"11.23pm\" or \"5 sec\"; REPEAT-EVERY-HOUR +is a floating-point number of hours to continuously repeat the +alert. OPEN is a URL that is opened when the user clicks the +notification. This can be a web or file URL, or any custom URL +scheme. + +I initially used optional arguments, but realised that in due time +it would be more informative to use named arguments instead. + +Example uses: + +;; In 5 minutes from now, remind me to watch this neato video! +(my/notify \"🔔 Get things done! 📎 💻 \" + :open \"https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity\" + :at \"5 minutes\") + ;; :at \"5 sec\" + +;; Remind me to exercise every 1.5hours; starting at 8:00am. +(my/notify \"Take a 5min break and get your blood flowing!\" + :titled \"Exercise\" + :at \"8:00am\" + :repeat-every-hour 1.5) + +;; Actually getting things done! +(my/notify \"Is what you're doing actually in alignment with your goals? + Maybe it's time to do another task?\" + :titled \"Check your agenda!\" + :at \"10:00am\" + :repeat-every-hour 2) + +" + (run-at-time at ;; the time to make the alert + (when repeat-every-hour (* 60 60 repeat-every-hour)) + #'async-shell-command + (format "%s" (s-replace "\n" "" + (s-join " " (--map (format "%s" it) + `(terminal-notifier + -title ,(pp-to-string titled) + -message ,(s-replace "\\n" "\n" (pp-to-string message)) + ;; Play a random sound when the notification appears. See sound names with: ls /System/Library/Sounds + ;; Use the special NAME “default” for the default notification sound. + -sound ,(progn (require 'seq) (seq-random-elt (s-split "\n" (shell-command-to-string "ls /System/Library/Sounds")))) + ;; Don't create duplicates of the notification, just one instance; + ;; i.e., each notification belongs to a group and only one alert of the group may be present at any one time. + -group ,(pp-to-string titled) + ;; Activate the application specified by ID when the user clicks the notification. + -activate org.gnu.Emacs + ,@(when open `(-open ,(pp-to-string open))) + ;; Run the shell command COMMAND when the user clicks the notification. + ;; -execute COMMAND + ;; & ;; … and then speak! … + ;; NOTE:This was getting annyoning in the middle of work meetings. + ;; say ,(s-replace "\\n" " " (pp-to-string message)) + ))))))) + +;; (Emojis look terrible in Lisp; but much better when the alert is actually made!) + +;; Remind me to exercise every 1.5hours; starting at 8:00am. +(my/notify "Take a 5min break and get your blood flowing!\n\t\t🚣 🏃‍♂️ 🧗‍♂️ 🧘‍♂️ 🏊 🏋 🚴‍♂️" + :titled "🤾‍♀️ Exercise 🚵‍♂️" + :at "8:00am" + :repeat-every-hour 1.5 + :open "https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity") + +;; Actually getting things done! +(my/notify "Is what you're doing actually in alignment with your goals? ✔️📉 + Maybe it's time to do another task? 📋" + :titled "📆 Check your agenda! 🔔" + :at "10:00am" + :repeat-every-hour 2) + +;; Make every other line of a buffer grey (or whatever you like). +;; Useful for buffers that list things. +;; I want it to make my Org tables look nice. Even better when org-modern is activated. +(use-package stripe-buffer + :defer 100 + :config (add-hook 'org-mode-hook 'turn-on-stripe-table-mode)) + +;; [[file:init.org::*highlight quoted symbols][highlight quoted symbols:1]] +(use-package highlight-quoted + :defer nil + :config (add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode)) + +;; If everything worked fine, then “ 'b ” below should be coloured nicely in Emacs Lisp mode. +(when nil + (-let [x 'somevar] + (list x 'b "c" :e))) +;; highlight quoted symbols:1 ends here + +;; [[file:init.org::*Bidirectional Text][Bidirectional Text:1]] +;; Sometimes I have Arabic in my buffers, however I am an English speaker +;; and so Left-to-Right is most natural to me. As such, even when Arabic +;; is present, or any bidirectional text, just use Left-to-Right. +(setq-default bidi-paragraph-direction 'left-to-right) +;; Bidirectional Text:1 ends here + ;; [[file:init.org::*Whitespace][Whitespace:1]] (add-hook 'before-save-hook 'whitespace-cleanup) ;; Whitespace:1 ends here @@ -1305,7 +2022,7 @@ the character 𝓍 before and after the selected text." ;; Fix spelling as you type ---thesaurus & dictionary too!:2 ends here ;; [[file:init.org::*Fix spelling as you type ---thesaurus & dictionary too!][Fix spelling as you type ---thesaurus & dictionary too!:3]] -(setq ispell-program-name "/usr/local/bin/aspell") +(setq ispell-program-name "/opt/homebrew/bin/aspell") (setq ispell-dictionary "en_GB") ;; set the default dictionary ;; Fix spelling as you type ---thesaurus & dictionary too!:3 ends here @@ -1336,6 +2053,7 @@ the character 𝓍 before and after the selected text." ;; [[file:init.org::*Fix spelling as you type ---thesaurus & dictionary too!][Fix spelling as you type ---thesaurus & dictionary too!:10]] (use-package synosaurus + :defer 100 :init (synosaurus-mode) :config (setq synosaurus-choose-method 'popup) ;; 'ido is default. (global-set-key (kbd "M-#") 'synosaurus-choose-and-replace)) @@ -1348,41 +2066,20 @@ the character 𝓍 before and after the selected text." ;; [[file:init.org::*Fix spelling as you type ---thesaurus & dictionary too!][Fix spelling as you type ---thesaurus & dictionary too!:12]] (use-package wordnut + :defer 100 :bind ("M-!" . wordnut-lookup-current-word)) ;; Use M-& for async shell commands. ;; Fix spelling as you type ---thesaurus & dictionary too!:12 ends here -;; [[file:init.org::*Using a Grammar & Style Checker][Using a Grammar & Style Checker:1]] -(use-package langtool - :defer t - :custom - (langtool-language-tool-jar - "~/Applications/LanguageTool-4.5/languagetool-commandline.jar")) -;; Using a Grammar & Style Checker:1 ends here - -;; [[file:init.org::*Using a Grammar & Style Checker][Using a Grammar & Style Checker:2]] -;; Quickly check, correct, then clean up /region/ with M-^ -(eval-after-load 'langtool -(progn -(add-hook 'langtool-error-exists-hook - (lambda () - (langtool-correct-buffer) - (langtool-check-done))) - -(global-set-key "\M-^" - (lambda () - (interactive) - (message "Grammar checking begun ...") - (langtool-check))))) -;; Using a Grammar & Style Checker:2 ends here - ;; [[file:init.org::*Lightweight Prose Proofchecking][Lightweight Prose Proofchecking:1]] (use-package writegood-mode ;; Load this whenver I'm composing prose. :hook (text-mode org-mode) ;; Don't show me the “Wg” marker in the mode line + :defer 100 + ;; Some additional weasel words. :config (--map (push it writegood-weasel-words) @@ -1395,17 +2092,17 @@ the character 𝓍 before and after the selected text." ;; Lightweight Prose Proofchecking:1 ends here ;; [[file:init.org::*Placeholder Text ---For Learning & Experimenting][Placeholder Text ---For Learning & Experimenting:1]] -(use-package lorem-ipsum :defer t) +(use-package lorem-ipsum ) ;; Placeholder Text ---For Learning & Experimenting:1 ends here ;; [[file:init.org::*Some text to make us smile][Some text to make us smile:1]] (use-package dad-joke - :defer t + :config (defun dad-joke () (interactive) (insert (dad-joke-get)))) ;; Some text to make us smile:1 ends here ;; [[file:init.org::*Unicode Input via Agda Input][Unicode Input via Agda Input:1]] -; (load (shell-command-to-string "agda-mode locate")) +;; (load (shell-command-to-string "agda-mode locate")) ;; ;; Seeing: One way to avoid seeing this warning is to make sure that agda2-include-dirs is not bound. ; (makunbound 'agda2-include-dirs) @@ -1418,10 +2115,16 @@ the character 𝓍 before and after the selected text." ;; [[file:init.org::*Unicode Input via Agda Input][Unicode Input via Agda Input:4]] (unless noninteractive (load-file (let ((coding-system-for-read 'utf-8)) - (shell-command-to-string "/usr/local/bin/agda-mode locate")))) + (shell-command-to-string "agda-mode locate")))) ;; Unicode Input via Agda Input:4 ends here ;; [[file:init.org::*Unicode Input via Agda Input][Unicode Input via Agda Input:5]] +;; TODO: Maybe don't bother installing Agda, and just get agda-input.el +;; from: https://github.com/agda/agda/blob/master/src/data/emacs-mode/agda-input.el +;; then loading that! +(url-copy-file "https://raw.githubusercontent.com/agda/agda/master/src/data/emacs-mode/agda-input.el" "~/.emacs.d/elpa/agda-input.el" :ok-if-already-exists) +(load-file "~/.emacs.d/elpa/agda-input.el") + ;; MA: This results in "Package cl is deprecated" !? (unless noninteractive (use-package agda-input @@ -1545,6 +2248,7 @@ the character 𝓍 before and after the selected text." ;; [[file:init.org::*Moving Text Around][Moving Text Around:1]] ;; M-↑,↓ moves line, or marked region; prefix is how many lines. (use-package move-text + :config (move-text-default-bindings)) ;; Moving Text Around:1 ends here @@ -1556,32 +2260,19 @@ the character 𝓍 before and after the selected text." (delete-selection-mode 1) ;; Delete Selection Mode:1 ends here -;; [[file:init.org::*Letter-based Navigation][Letter-based Navigation:1]] -(use-package ace-jump-mode - :defer t - :config (bind-key* "C-c SPC" 'ace-jump-mode)) - -;; See ace-jump issues to configure for use of home row keys. -;; Letter-based Navigation:1 ends here - -;; [[file:init.org::*Letter-based Navigation][Letter-based Navigation:2]] -;; C-x o ⇒ Switch to the other window -;; C-x O ⇒ Switch back to the previous window -(bind-key "C-x O" (lambda () (interactive) (other-window -1))) -;; Letter-based Navigation:2 ends here - ;; [[file:init.org::*visual-regexp][visual-regexp:1]] ;; While constructing the regexp in the minibuffer, get live visual feedback for the (group) matches. ;; E.g., try: M-% use-\(.+?\) \(.+\)\b ENTER woah \1 and \2 ;; ;; C-u M-% do to regexp replace, without querying. (use-package visual-regexp + :config (define-key global-map (kbd "M-%") (lambda (&optional prefix) (interactive "P") (call-interactively (if prefix #'vr/replace #'vr/query-replace))))) ;; visual-regexp:1 ends here ;; [[file:init.org::*HTML ⇐ Org-mode][HTML ⇐ Org-mode:1]] -(use-package htmlize :defer t) +(use-package htmlize ) ;; Main use: Org produced htmls are coloured. ;; Can be used to export a file into a coloured html. ;; HTML ⇐ Org-mode:1 ends here @@ -1682,13 +2373,9 @@ by spaces. (setq org-html-format-drawer-function 'my/org-drawer-format) ;; HTML “Folded Drawers”:1 ends here -;; [[file:init.org::*Diagrams with Mermaid ---Not Reccommended][Diagrams with Mermaid ---Not Reccommended:2]] -(use-package ob-mermaid - :custom ob-mermaid-cli-path "~/node_modules/.bin/mmdc") -;; Diagrams with Mermaid ---Not Reccommended:2 ends here - ;; [[file:init.org::*  \[\[https:/revealjs.com/?transition=zoom#/\]\[Reveal.JS\]\] -- The HTML Presentation Framework][  [[https://revealjs.com/?transition=zoom#/][Reveal.JS]] -- The HTML Presentation Framework:1]] (use-package ox-reveal + :custom (org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")) ;;   [[https://revealjs.com/?transition=zoom#/][Reveal.JS]] -- The HTML Presentation Framework:1 ends here @@ -1701,16 +2388,17 @@ by spaces. ") ;;   [[https://revealjs.com/?transition=zoom#/][Reveal.JS]] -- The HTML Presentation Framework:3 ends here -;; [[file:init.org::*Org-mode ⇐ HTML][Org-mode ⇐ HTML:2]] +;; [[file:init.org::*C-c C-l Org-mode ⇐ HTML][C-c C-l Org-mode ⇐ HTML:2]] (use-package org-web-tools + :config ;; Insert an Org-mode link to the URL in the clipboard or kill-ring. Downloads ;; the page to get the HTML title. ;; (bind-key* "C-c C-l" #'org-web-tools-insert-link-for-url) ;; Instead, see my/org-insert-link-dwim below. ) -;; Org-mode ⇐ HTML:2 ends here +;; C-c C-l Org-mode ⇐ HTML:2 ends here -;; [[file:init.org::*Org-mode ⇐ HTML][Org-mode ⇐ HTML:3]] +;; [[file:init.org::*C-c C-l Org-mode ⇐ HTML][C-c C-l Org-mode ⇐ HTML:3]] ;; C-u C-c C-l ⇒ Paste URL with title, WITHOUT prompting me for anything. ;; C-c C-l ⇒ Prompt me for title. (bind-key* "C-c C-l" @@ -1754,7 +2442,795 @@ Functin Source: https://xenodium.com/emacs-dwim-do-what-i-mean/" 'title)))))))) (t (call-interactively 'org-insert-link))))) -;; Org-mode ⇐ HTML:3 ends here +;; C-c C-l Org-mode ⇐ HTML:3 ends here + +;; [[file:init.org::*loading work.el][loading work.el:1]] +(load-file "~/Desktop/work.el") +;; loading work.el:1 ends here + +;; [[file:init.org::*RDD][RDD:1]] +(setq enable-local-variables :safe) + +(use-package repl-driven-development) +(setq repl-driven-development/echo-duration 10) ;; seconds + +;; Sometimes I see a bunch of shell incantations in a README or something and I'd like to execute them right there and then, +;; and not have to bother with copying them over to a terminal and execute there. As such, here's a quick key binding to execute +;; shell commands from anywhere. +(repl-driven-development [C-x C-t] "bash" :prompt "bash-3.2\\$") +(when nil ;; example +NAME=musa +echo hello $NAME +echo hello $CLASSPATH +) + + +;; Set “C­x C­j” to evaluate Java code in a background REPL. +(repl-driven-development [C-x C-j] + ;; enable assertions, and add everything installed, via mvn, in scope; and imports all Java SE packages by default! + (format "jshell --class-path %s --enable-preview -R -ea" + (shell-command-to-string "find ~/.m2/repository -name \"*.jar\" -type f | tr '\n' ':'")) + :prompt "jshell>") +;; Do not use the “--startup JAVASE” option. +;; +;; List.of(1) | Error: +;; | reference to List is ambiguous +;; | both interface java.util.List in java.util and class java.awt.List in java.awt match +;; | List.of(1) +;; | ^--^ +;; + +(defun mvn (groupId artifactId) + "Quickly install a library from Maven Central." + (async-shell-command (format "mvn org.apache.maven.plugins:maven-dependency-plugin:2.8:get -Dartifact=%s:%s:LATEST:jar:sources" groupId artifactId))) +(when nil ;; Example use of C-x C-j + + 1 + 22 + + TestNG is a testing framework inspired from JUnit and NUnit but introducing some new functionalities that make it more powerful and easier to use. It supports test configured by annotations, data-driven testing, parametric tests, etc. +(mvn "org.testng" "testng") + +import org.testng.*; + /imports // Now can see org.testng at the end of the list + +java -jar /Users/musa/.m2/repository/org/antlr/antlr4/4.13.0/antlr4-4.13.0-complete.jar + +(mvn "org.antlr" "antlr4") ;; C-x C-e +;; Now re-start the Java C-x C-j repl via a C-x C-e (lame! not ergonomic!) +;; Now check that you have access to antrl in your repl by importing it and looking at one of its classes: +;; ⦃ jshell --class-path /Users/musa/.m2/repository/org/antlr/antlr4-runtime/4.13.0/antlr4-runtime-4.13.0.jar ⦄ +import org.antlr.v4.runtime.*; +CommonTokenStream.class +;; MA: This is the runtime, I want the actual tool! +;; +;; +;; MA: to be ergonomic, make “C-u -1 C-x C-j” restart the repl, by *default* this means kill the repl process and restart it. +;; +;; MA: TODO: For working with Lombok annotations, use the jshell `/reset --class-path` command to include the lombok compiled file into +;; the current Jshell session. +;; +;; See: https://stackoverflow.com/questions/74084364/how-to-use-lombok-in-jshell +;; For example, +;; +;; $ jshell +;; > var x = 5 +;; > import org.antlr.v4.runtime.*; // CRASHES since antlr is not in scope +;; > /reset --class-path /Users/musa/.m2/repository/org/antlr/antlr4-runtime/4.13.0/antlr4-runtime-4.13.0.jar +;; > import org.antlr.v4.runtime.*; // WORKS, yay +;; > x // CRASHES, not in scope +;; +;; (mvn "org.projectlombok" "lombok") +;; import lombok.*; +;; @lombok.Data class Test { private String name; } +;; new Test().equals(new Test()) + +import org.apache.commons.lang3.StringUtils; +StringUtils.class; +/imports // JShell command to list all imports, it now contains apache! + + +// Guava is the Google core libraries for Java +import com.google.common.collect.ImmutableMap; + +ImmutableMap.of(1, "A", 2, "B") // ⇒ {1=A, 2=B} + +// (mvn "com.google.code.gson" "gson") +// Then C-x C-e to update the repl definition of C-x C-j to include the updated gson library. +import com.google.gson.*; +String json = new Gson().toJson(Map.of("me", List.of(1, 2,3), "you", Map.of("Love", "Lisp", "Hate", "Verbosity"))) + +;; End examples +) +;; RDD:1 ends here + +;; [[file:init.org::*Adding support for “/!use” & “/!omit” top level repl commands][Adding support for “//!use” & “//!omit” top level repl commands:1]] +;; Adding support for “//!use” & “//!omit” top level repl commands +(setq repl/jshell/classpath (shell-command-to-string "find ~/.m2/repository -name \"*.jar\" -type f | tr '\n' ':'")) +(advice-add 'repl/jshell + :around (lambda (repl &rest args) + (if (equal nil current-prefix-arg) + ;; No prefix supplied + (progn + (setq rdd---current-input (s-replace-regexp "\n" "" (s-trim-left + (if (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end)) + (substring-no-properties (thing-at-point 'line)))))) + (if (s-starts-with? "//!use" rdd---current-input) + (-let [jar (s-trim (s-chop-prefix "//!use" rdd---current-input))] + (repl/top-level//!use jar)) + (if (s-starts-with? "//!omit" rdd---current-input) + (-let [jar (s-trim (s-chop-prefix "//!omit" rdd---current-input))] + (repl/top-level//!omit jar)) + ;; otherwise business as usual + (apply repl args)))) + (pcase current-prefix-arg + (-1 + ;; reset classpath to default, then business as usual. + (setq repl/jshell/classpath (shell-command-to-string "find ~/.m2/repository -name \"*.jar\" -type f | tr '\n' ':'")) + (apply repl args)) + (999 + (message-box "It worked")) + ;; otherwise business as usual + (t (apply repl args)))))) + +;; remove all advice +;; (-let [sym 'repl/jshell] (advice-mapc (lambda (advice _props) (advice-remove sym advice)) sym)) + +(cl-defun repl/top-level//!omit (str) + (with-temp-buffer + (setq repl/jshell/classpath (s-replace-regexp (format ":[^:]*%s[^:]*:" str) ":" repl/jshell/classpath)) + (insert "/env --class-path ") + (insert repl/jshell/classpath) + (repl/jshell (point-min) (point-max)))) + +(cl-defun repl/top-level//!use (str) + "If the given jar cannot be added successful, the existing classpath remains untouched. + +Return to your default classpath by invoking the repl with the -1 prefix. + +Example usage: + + //!use ~/path/to/compiled/java/classes + import com.x.y.z; + +Where directory hierarchy com/x/y/z denotes a Java package under the above //!use path. +" + (with-temp-buffer + (setq repl/jshell/classpath (concat (s-trim str) ":" repl/jshell/classpath)) + (insert "/env --class-path ") + (insert repl/jshell/classpath) + (repl/jshell (point-min) (point-max)))) +;; Adding support for “//!use” & “//!omit” top level repl commands:1 ends here + +;; [[file:init.org::*jRead][jRead:1]] +(add-hook 'repl-driven-development/output-hook + (lambda (output) + (when (equal current-prefix-arg '(4)) ;; insertion prefix + (insert "\n") + (insert (jRead (s-replace-regexp "^.* ==> " "" repl-driven-development-current--output))) + ;; Disable default insertion behaviour. + (setq current-prefix-arg nil)) + output)) +;; +;; +;; TODO: Add to java init/prologue +;; JShell has a truncation for outputs exceeding 1k chars, let's increase the threshold to size 40k chars. +;; /set truncation 40000 +;; +;; TODO: Add to docs of repl-driven-development-current--output, that if you see “...” then chances are that you have +;; exceeded the truncation threshold for your repl. Consider increasing the threshold, if possible, and add it to the :init/:prologue of your repl. +;; +;; +;; record Person(String name, int age, double zindex) { } +;; var people = List.of(new Person("Jasim", 72, .5), new Person("Kathy", 82, 2.78), new Person("Al-hassy, Musa", 31, 1 + 2)) +;; people // Press C-u C-x C-j to get the executable Java represenation inserted +(cl-defun jRead (a-pretty-printed-record--diff) + "Reads executable Java code from pretty-printed record representations. + +If called interactively, copies executable Java to clipboard; otherwise +returns the executable Java as a string." + (interactive "sJava Read: ") + (let* ((sep (format " ⟨%s⟩ " (gensym))) + (rx-sep (regexp-quote sep)) + (result + (--> + a-pretty-printed-record--diff + ;; "[Person[name=Jasim, age=72, zindex=.5], Person[name=Kathy, age=82, zindex=2.78], Person[name=Musa, age=31, zindex=3]]" + ;; "Person[name=Al-hassy, Musa, age=thirty and one, years=31]" + ;; Identify field delimiters from commas that may be present in the payloads. + ;; Drop all field names from pretty-printed records: `RecordName[fieldName=value goes to `RecordName[value]` + (s-replace-regexp ", [^ \\[]*=" (concat sep sep) it) ;; ⇒⇒⇒ "Person[name=Al-hassy, Musa ⟨g760⟩ ⟨g760⟩ thirty and one ⟨g760⟩ ⟨g760⟩ 31]" + ;; Replace `RecordName[value]` with `new RecordName(value]` + (s-replace-regexp "\\([A-Za-z]+\\)\\[[^ ]*=" (concat "new \\1(" sep) it :lower-case) ;; ⇒⇒⇒ "new Person( ⟨g761⟩ Al-hassy, Musa ⟨g761⟩ ⟨g761⟩ thirty and one ⟨g761⟩ ⟨g761⟩ 31]" + ;; All remaining `]` can now be replaced by `)`. + (s-replace-regexp "\\]" (concat sep ")") it) ;; ⇒⇒⇒ "new Person( ⟨g779⟩ Al-hassy, Musa ⟨g779⟩ ⟨g779⟩ thirty and one ⟨g779⟩ ⟨g779⟩ 31 ⟨g779⟩ )" + ;; As a precautionary measure, let's enclose all arguments in quotes + (s-replace-regexp (format "%s\\(.*?\\)%s" rx-sep rx-sep) ", \"\\1\"" it) ;; ⇒⇒⇒ "new Person(, \"Al-hassy, Musa\", \"thirty and one\", \"31\")" + ;; Remove quotes around numbers + (s-replace-regexp "\"\\([0-9.]+.?[0-9.]*\\)\"" "\\1" it) ;; ⇒⇒⇒ "new Person(,\"Al-hassy, Musa\",\"thirty and one\",31)" + ;; Remove malformed “(,” + (s-replace-regexp "(, " "(" it) + (s-replace-regexp (concat sep ")") ")" it) + ;; All remaining `[` are now `List.of` pretty-printings + (s-replace-regexp "\\[" "List.of(" it :lower-case)))) + (if (called-interactively-p) + (progn (kill-new result) + (message "Copied: %s" result)) + result))) + +(ert-deftest jRead-works-as-intended () + (should (equal (jRead "1 + 2") "1 + 2")) + (should (equal (jRead "Person[name=Musa]") "new Person(\"Musa\")")) + (should (equal (jRead "Person[name=Al-hassy, Musa, age=thirty and one, years=31]") "new Person(\"Al-hassy, Musa\", \"thirty and one\", 31)")) + (should (equal (jRead "[Person[name=Jasim, age=72, zindex=.5], Person[name=Kathy, age=82, zindex=2.78], Person[name=Musa, age=31, zindex=3]]") + "List.of(new Person(\"Jasim\", 72, .5), new Person(\"Kathy\", 82, 2.78), new Person(\"Musa\", 31, 3))"))) +;; jRead:1 ends here + +;; [[file:init.org::*jRead][jRead:2]] +;; Other DSL rewrites I want NOW + +(cl-defun jRead/rewrite (java-code) + (interactive "s") + (--> java-code + (loop for (rx replacement) in jRead-defs + do (setq it (s-replace-regexp rx replacement it)) + finally return it) + ;; (my/format-code #'java-mode it) ;; TODO: Not actually running; async issue probably. + kill-new + (yank))) + +;; Other DSL rewrites I want NOW +(defvar jRead-defs nil + "List of regexp-to-replacement pairs to apply to Java code.") + +;; (add-to-list 'jRead-defs '("new X(List.of(new Y(\\(.*?\\))))" "xy(\\1)")) + +(defun my/format-code (language-mode text) + "Format TEXT according to the given LANGUAGE-MODE. + +Example: + + ;; The following results in the standard 5-line convention for an if-then-else clause in Java + (my/format-code #'java-mode \"if(true) { System.out.println(2); } else { System.out.println(3); } \") + +The tool needed to do the formatting is specified in +`format-all-default-formatters', so ensure it's installed! +" + (with-temp-buffer + ;; (java-mode) + (funcall language-mode) + ;; Don't prompt me which formatter to use; just use the defaults. + ;; For example, for Java, I need to: brew install clang-format + (setq format-all-formatters format-all-default-formatters) + ;; (insert "cmd(new TclCat(List.of(new TclAtom(\"cmd4\"))), new TclQuote(List.of(new TclCat(List.of(new TclAtom(\"arg5\"))))), new TclCat(List.of(new TclAtom(\"$arg6\"))), new TclBrace(List.of(new TclCat(List.of(new TclAtom(\"a\"))), new TclCat(List.of(new TclAtom(\"r\"))), new TclCat(List.of(new TclAtom(\"g\"))), new TclCat(List.of(new TclAtom(\"7\"))))))") + (insert text) + (format-all-buffer) + (buffer-substring-no-properties (point-min) (point-max)))) +;; jRead:2 ends here + +;; [[file:init.org::*Programming][Programming:1]] +(when my/work-machine? + (setq doom-modeline-buffer-file-name-style 'truncate-except-project)) +;; Programming:1 ends here + +;; [[file:init.org::*Sleek Semantic Selection][Sleek Semantic Selection:1]] +(use-package expand-region + + :bind (("s-r" . #'er/expand-region))) +;; Sleek Semantic Selection:1 ends here + +;; [[file:init.org::*Project management & navigation][Project management & navigation:1]] +;; More info & key bindings: https://docs.projectile.mx/projectile/usage.html +(use-package projectile + + :config + (projectile-mode +1) + (define-key projectile-mode-map (kbd "C-x p") 'projectile-command-map) + + ;; Replace usual find-file with a project-wide version :smile: + (global-set-key (kbd "C-x f") #'projectile-find-file) + + ;; Makes indexing large projects much faster, after first time. + ;; Since its caching, some files may be out of sync; you can delete the cache + ;; with: C-u C-x f + (setq projectile-enable-caching t)) + +(use-package projectile + + :config + (define-key projectile-mode-map (kbd "C-x p s") + ;; I prefer helm-do-grep-ag since it shows me a live search + (lambda () (interactive) + (let ((default-directory (car (projectile-get-project-directories (projectile-acquire-root))))) + ;; (shell-command-to-string "echo $PWD") + (helm-do-grep-ag nil))))) ;; “p”roject “s”earch +;; Project management & navigation:1 ends here + +;; [[file:init.org::*Project management & navigation][Project management & navigation:2]] +(use-package projectile + :defer nil + :config +(define-key projectile-mode-map (kbd "C-x p c") + (defun my/copy-current-file-path () + "Add current file path to kill ring." + (interactive) + (message (kill-new buffer-file-name))))) +;; Project management & navigation:2 ends here + +;; [[file:init.org::*Projectile][Projectile:1]] +;; https://cestlaz.github.io/posts/using-emacs-33-projectile-jump/ +;; https://github.com/bbatsov/projectile +(use-package projectile + +:config (projectile-global-mode) +(define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)) +;; Projectile:1 ends here + +;; [[file:init.org::*Aggressive Indentation][Aggressive Indentation:1]] +;; Always stay indented: Automatically have blocks reindented after every change. +(use-package aggressive-indent + :defer nil + :config (global-aggressive-indent-mode t)) + +;; Use 4 spaces in places of tabs when indenting. +(setq-default indent-tabs-mode nil) +(setq-default tab-width 4) +;; Aggressive Indentation:1 ends here + +;; [[file:init.org::*Coding with a Fruit Salad: Semantic Highlighting][Coding with a Fruit Salad: Semantic Highlighting:1]] +(use-package color-identifiers-mode + + :config (global-color-identifiers-mode)) + +;; Sometimes just invoke: M-x color-identifiers:refresh +;; Coding with a Fruit Salad: Semantic Highlighting:1 ends here + +;; [[file:init.org::*Text Folding ---Selectively displaying portions of a program][Text Folding ---Selectively displaying portions of a program:1]] +(use-package vimish-fold + + :config (vimish-fold-global-mode 1)) +;; Text Folding ---Selectively displaying portions of a program:1 ends here + +;; [[file:init.org::*Actual Setup][Actual Setup:1]] +(use-package hideshow + :defer nil + :init + ;; https://github.com/emacsmirror/emacswiki.org/blob/master/hideshowvis.el + (quelpa '(hideshowvis :fetcher wiki)) + + ;; Press “C-c TAB” to toggle a block's visibility or “C-c f” for my folding hydra. + :bind (("C-c TAB" . hs-toggle-hiding)) + + ;; https://github.com/shanecelis/hideshow-org/tree/master + ;; This extension bring Org-mode tab behaviour to folding, at the block level + ;; and buffer level ---but not cycling visibility. + ;; (use-package hideshow-org) ;; Disabled as commented below. + + :hook ((prog-mode . (lambda () (hs-minor-mode +1) + (hideshowvis-minor-mode t) + (hideshowvis-symbols) + ;; This hook along with hs-org mode breaks editing of src blocks in Org files. + ;; That's OK, since my folding hydra does a better job for my needs. + ;; (hs-org/minor-mode t) + (hs-hide-all))))) +;; Actual Setup:1 ends here + +;; [[file:init.org::*Actual Setup][Actual Setup:2]] +(my/defhydra "C-c f" "Folding text" archive + :Current + ("h" hs-hide-block "Hide") + ("s" hs-show-block "Show") + ("t" hs-toggle-hiding "Toggle") + ;; "l" hs-hide-level "Hide blocks n levels below this block"; TODO: Enable folding feature + :Buffer + ("H" hs-hide-all "Hide") + ("S" hs-show-all "Show") + ("T" my/hs-toggle-buffer "Toggle") + :Style + ("i" my/clever-selective-display "Fold along current indentation" :toggle selective-display) + ("e" auto-set-selective-display-mode "Explore; walk and see" :toggle t) + :Region + ("f" (lambda () (interactive) (vimish-fold-toggle) (vimish-fold (region-beginning) (region-end))) "Fold/Toggle") + ("d" vimish-fold-delete "Delete fold") + ("U" vimish-fold-unfold-all "Unfold all") + ("D" vimish-fold-delete-all "Delete all") + ("n" vimish-fold-next-fold "Next fold") + ("p" vimish-fold-previous-fold "Previous fold") + :... + ("w" hl-todo-occur "Show WIPs/TODOs" :exit t) + ("m" lsp-ui-imenu "Menu of TLIs" :exit t) ;; TLI ≈ Top Level Items + ;; ("i" imenu-list "iMenu (General)") ;; It seems the above is enough for both prog and otherwise. + ("r" (progn (hs-minor-mode -1) (hs-minor-mode +1)) "Reset Hideshow") ;; Remove all folds from the buffer and reset all hideshow-mode. Useful if it messes up! + ("q" nil "Quit" :color blue)) + +;; Features from origami/yafolding that maybe I'd like to implement include: +;; narrowing to block or folding everything except block, navigating back and forth between folded blocks. +;; Finally, if we want to cycle the visibility of a block (as in Org-mode), we can use a combination of hs-show-block and hs-hide-level. +;; Actual Setup:2 ends here + +;; [[file:init.org::*Actual Setup][Actual Setup:3]] +(defvar my/hs-hide nil "Current state of hideshow for toggling all.") +(defun my/hs-toggle-buffer () "Toggle hideshow all." + (interactive) + (setq my/hs-hide (not my/hs-hide)) + (if my/hs-hide + (hs-hide-all) + (hs-show-all))) +;; Actual Setup:3 ends here + +;; [[file:init.org::*Actual Setup][Actual Setup:4]] +(defun my/clever-selective-display (&optional level) +"Fold text indented same of more than the cursor. + +This function toggles folding according to the level of +indentation at point. It's convenient not having to specify a +number nor move point to the desired column. +" + (interactive "P") + (if (eq selective-display (1+ (current-column))) + (set-selective-display 0) + (set-selective-display (or level (1+ (current-column)))))) +;; Actual Setup:4 ends here + +;; [[file:init.org::*Actual Setup][Actual Setup:5]] +;; Src: https://emacs.stackexchange.com/questions/52588/dynamically-hide-lines-indented-more-than-current-line +(define-minor-mode auto-set-selective-display-mode + "Automatically apply `set-selective-display' at all times based on current indentation." + nil "$" nil + (if auto-set-selective-display-mode + (add-hook 'post-command-hook #'auto-set-selective-display nil t) + (remove-hook 'post-command-hook #'auto-set-selective-display t) + (with-temp-message "" + (set-selective-display nil)))) +;; +(defun auto-set-selective-display () + "Apply `set-selective-display' such that current and next line are visible. + +Scroll events are excluded in order to prevent wild flickering while navigating." + (unless (eq last-command #'mwheel-scroll) + (let*((this-line-indent (current-indentation)) + (next-line-indent (save-excursion (forward-line) (current-indentation)))) + (with-temp-message "" ; Suppress messages. + (set-selective-display (1+ (max this-line-indent next-line-indent))))))) +;; Actual Setup:5 ends here + +;; [[file:init.org::*Actual Setup][Actual Setup:6]] +;; Open folded nodes if a search stops there. +(add-hook 'helm-swoop-after-goto-line-action-hook #'my/search-hook-function) +(defun my/search-hook-function () + (when hs-minor-mode (set-mark-command nil) (hs-show-block) (pop-to-mark-command))) +;; Actual Setup:6 ends here + +;; [[file:init.org::*hr: \[\[https:/github.com/LuRsT/hr\]\[A horizontal for your terminal\]\]][hr: [[https://github.com/LuRsT/hr][A horizontal for your terminal]]:1]] +(system-packages-ensure "hr") ;; ≈ brew install hr +;; hr: [[https://github.com/LuRsT/hr][A horizontal for your terminal]]:1 ends here + +;; [[file:init.org::*w-screencapture][w-screencapture:1]] +(bind-key "C-c s" + (cl-defun w-screencapture () + "Interactively capture screen and save to clipboard; then paste in Slack, etc, with ⌘-c. + + After we run this command, we can swipe up on mousepad to select different desktops, then + click & drag to select portition of screen to capture. + + Captured screen is NOT saved to disk, only copied to clipboard. + +In MacOs, ++ Command + Shift + 5 ⇒ Select screen record ++ Command + Shift + 4 ⇒ Selection Screenshot ++ Command + Shift + 3 ⇒ Screenshot + +See: https://osxdaily.com/2011/08/11/take-screen-shots-terminal-mac-os-x" + (interactive) + (async-shell-command "screencapture -i -c"))) + +(cl-defun w-delete-all-screenshots () + "Delete all “Screen Shot ⋯” files in ~/Desktop." + (interactive) + (thread-last (shell-command-to-string "cd ~/Desktop; ls") + (s-split "\n") + (--filter (s-starts-with-p "Screen Shot" it)) + (--map (f-delete (format "~/Desktop/%s" it))))) +;; w-screencapture:1 ends here + +;; [[file:init.org::*Comment-boxes up to the fill-column ---or banner instead?][Comment-boxes up to the fill-column ---or banner instead?:1]] +(defun my/comment-box (b e) + "Draw a box comment around the region but arrange for the region +to extend to at least the fill column. Place the point after the +comment box. + +Source: http://irreal.org/blog/?p=374 + +To do fancy stuff like removing boxes, centering them, etc +see https://github.com/lewang/rebox2/blob/master/rebox2.el" + (interactive "r") + (let ((e (copy-marker e t))) + (goto-char b) + (end-of-line) + (insert-char ? (- fill-column (current-column))) + (comment-box b e 1) + (goto-char e) + (set-marker e nil))) +;; Comment-boxes up to the fill-column ---or banner instead?:1 ends here + +;; [[file:init.org::*Comment-boxes up to the fill-column ---or banner instead?][Comment-boxes up to the fill-column ---or banner instead?:2]] +(use-package banner-comment :defer nil) +;; Comment-boxes up to the fill-column ---or banner instead?:2 ends here + +;; [[file:init.org::*Searching Hydra][Searching Hydra:1]] +(my/defhydra "s-f" "\t\tLocate Everything" search + :Buffer + ;; find all the occurrences of a string, pull out the lines containing the string to another buffer where [F2] I can edit and save, + ("e" helm-swoop "Editable") + ;; Implicit Regex, colourful + ("c" swiper "Classic") + + :Project + ;; “:toggle ℰ”: ℰ is a Boolean expression that is evaluated to tell us whether the state is on-or-off + ("t" (lambda () (interactive)) "Ignore specs/jsons" + :toggle (let* ((with-hole "ag %s --line-numbers -S --color --nogroup %%s %%s %%s") ;; ≈ original value of ‘helm-grep-ag-command’ + (ignores "--ignore=\"*spec.js\" --ignore=\"*.json\" --ignore=\"*.json5\"") + (on (equal helm-grep-ag-command (format with-hole ignores)))) + (if on (progn (setq helm-grep-ag-command (format with-hole "")) nil) ;; ≈ turn off the toggle + (setq helm-grep-ag-command (format with-hole ignores))))) + ("f" (lambda () (interactive) (helm-do-grep-ag t)) "File type") + ("d" (lambda () (interactive) (-let [default-directory (read-directory-name "Where do you want to search? ")] (helm-do-grep-ag nil))) "Directory") + ("D" (lambda () (interactive) (-let [default-directory (read-directory-name "Where do you want to search? ")] (helm-do-grep-ag t))) "Directory & type")) +;; Searching Hydra:1 ends here + +;; [[file:init.org::*⌘-e: Edit Everything in a separate buffer][⌘-e: Edit Everything in a separate buffer:1]] +(use-package separedit :defer nil) +;; +;; # Example Usage +;; +;; 1. Press ⌘-e on this line, to edit this entire comment. +;; 2. Press ⌘-e to exit the edit session. +;; +;; Since my ⌘-e is context sensitive, to determine whether to continue editing or +;; exit; you can explicitly request an edit with C-c ' and an exit with C-c C-c. +;; +;; ``` +;; ;; 3. Press ⌘-e on this line, to edit this source block! +;; ;; 4. Press ⌘-e on this line, to edit this inner-most comment! +;; ;; 5. At start of next line, press “⌘-r ⌘-e” to edit just the source block +;; ;; +;; (cl-defun index (&rest args) +;; "6. Press ⌘-e to edit this string, \"7. and again in these quotes\"" +;; "

8. Press ⌘-e to edit this HTML block, in Web-mode

") +;; +;; ;; 9. Press C-u ⌘-e to guess the language of the next string (Rust); then ⌘-r C-c C-r to quickly run the code. +;; "fn main() { println!(\"{}\", \"hello!\"); }" +;; +;; ;; 10. Select & press “C-u ⌘-e” on the following, to edit it in whatever mode you want. +;; ;; select * from table -- Or just press ⌘-e and have the mode detected. +;; +;; ``` + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Setup to make the above ⌘-e behaviour happen. + +;; Make “⌘-e” toggle editing string literals / select region / [Org/markdown] code block / comment block when programming. +(--map (bind-key "s-e" #'separedit it) + '(prog-mode-map minibuffer-local-map help-mode-map)) ;; TODO: helpful-mode-map +;; ⌘-e: Edit Everything in a separate buffer:1 ends here + +;; [[file:init.org::*⌘-e: Edit Everything in a separate buffer][⌘-e: Edit Everything in a separate buffer:2]] +;; TODO:Merge these changes upstream + +;; I'm focusing on a specific region to edit, so let's not be distracted by anything else. +;; This makes the “editing stack” feel like a stack, with ⌘-e pushing new editing session buffers, +;; and C-c C-c, or ⌘-e on non-editable lines, to pop-off the stack. +;; (advice-add #'separedit :after (lambda (&rest _) (delete-other-windows))) +;; +;; NOTE: This actually breaks the stack nature of popping with ⌘-e; we need to actually save the stack via some list of buffers than push/pop buffers on that variable. + +;; I don't want to be bothered for what mode I'm in, when a region is selected using current major mode. +;; I'll use a prefix, “C-u ⌘-e”, if I want to select a mode for my current selected text. +(advice-add #'separedit--select-mode :before-until + (lambda (&rest _) + (when (and (not current-prefix-arg) (region-active-p)) (pp-to-string major-mode)))) + +;; Also: When on a string ∷ +(advice-add #'separedit--select-mode :before-until + (lambda (&rest _) + "When on a string ∷ ++ ⌘-e ⇒ Edit string at point ++ C-u ⌘-e ⇒ Auto-detect my string's major mode ++ C-u C-u ⌘-e ⇒ Let me select a major mode" + (-let [str? (ignore-errors (thing-at-point 'string))] + (case (car current-prefix-arg) + (4 (when str? (pp-to-string (my/detect-prog-mode str?)))) + (_ nil))))) + +;; NOTE: By default, separedit provides colouring for 'strings', "strings", and `strings' +;; This doesn't look very good when I have a single quote within double quotes: +;; In an Emacs Lisp buffer, editing the string "Bob's Work" gives unexpected highlighting. +;; ``` +;; (advice-add #'separedit :after +;; (lambda (&rest _) +;; (when (s-ends-with? "string-mode" (pp-to-string major-mode)) +;; (text-mode)))) +;; ``` +;; ⌘-e: Edit Everything in a separate buffer:2 ends here + +;; [[file:init.org::*⌘-e: Edit Everything in a separate buffer][⌘-e: Edit Everything in a separate buffer:3]] +;; In the indirect buffer, make ⌘-e finish editing. +(use-package edit-indirect + :config (bind-key "s-e" + (lambda () + (interactive) + (or (ignore-errors (call-interactively #'separedit)) + (call-interactively #'edit-indirect-commit))) + #'edit-indirect-mode-map)) + +;; I also have “s-e” bound to `org-edit-src-exit'. +(advice-add 'org-edit-src-exit :before-until + (lambda (&rest r) + (when (ignore-errors (separedit)) t))) +;; ⌘-e: Edit Everything in a separate buffer:3 ends here + +;; [[file:init.org::*⌘-e: Edit Everything in a separate buffer][⌘-e: Edit Everything in a separate buffer:4]] +;; → ⌘-e on an Org paragraph pops-up an edit session in Org mode. +;; → ⌘-e on a selection in Org mode pops-up an edit session in Org mode. +;; TODO: Consider forming an alist for special blocks to refer to their preferred +;; edit mode, defaulting to Org-mode? Perhaps something to consider /after/ +;; addressing the bug below. +;; (advice-unadvice 'org-edit-special) MA: TODO: FIXME: Delete this? +(advice-add 'org-edit-special :around + (lambda (orginal-function &rest r) + (cond + ((region-active-p) (call-interactively #'edit-indirect-region) (org-mode)) + ((equal 'paragraph (car (org-element-at-point))) + (mark-paragraph) (call-interactively #'edit-indirect-region) (org-mode)) + (t (or (ignore-errors (apply orginal-function r)) + ;; We try to edit a special block when orginal-function fails. + ;; This way src blocks are not confused with the more generic idea of special blocks. + (when + (my/org-in-any-block-p) + ;; Note using org-element-at-point doesn't work well with special blocks when you're somewhere within the block. + ;; It only works correctly when you're on the boundary of the special block; which is not ideal. + ;; This is why I'm not using: (org-element-property :begin elem). + (-let [(start . end) (my/org-in-any-block-p)] + (set-mark-command start) + (goto-char end) (previous-line 2) (end-of-line) ;; FIXME: Still shows #+end_XXX for some reason. + (call-interactively #'edit-indirect-region) (org-mode)))))))) +;; ⌘-e: Edit Everything in a separate buffer:4 ends here + +;; [[file:init.org::*⌘-e: Edit Everything in a separate buffer][⌘-e: Edit Everything in a separate buffer:5]] +;; where... +(defun my/org-in-any-block-p () + "Return non-nil if the point is in any Org block. + +The Org block can be *any*: src, example, verse, etc., even any +Org Special block. + +This function is heavily adapted from `org-between-regexps-p'. + +Src: https://scripter.co/splitting-an-org-block-into-two/" + (save-match-data + (let ((pos (point)) + (case-fold-search t) + (block-begin-re "^[[:blank:]]*#\\+begin_\\(?1:.+?\\)\\(?: .*\\)*$") + (limit-up (save-excursion (outline-previous-heading))) + (limit-down (save-excursion (outline-next-heading))) + beg end) + (save-excursion + ;; Point is on a block when on BLOCK-BEGIN-RE or if + ;; BLOCK-BEGIN-RE can be found before it... + (and (or (org-in-regexp block-begin-re) + (re-search-backward block-begin-re limit-up :noerror)) + (setq beg (match-beginning 0)) + ;; ... and BLOCK-END-RE after it... + (let ((block-end-re (concat "^[[:blank:]]*#\\+end_" + (match-string-no-properties 1) + "\\( .*\\)*$"))) + (goto-char (match-end 0)) + (re-search-forward block-end-re limit-down :noerror)) + (> (setq end (match-end 0)) pos) + ;; ... without another BLOCK-BEGIN-RE in-between. + (goto-char (match-beginning 0)) + (not (re-search-backward block-begin-re (1+ beg) :noerror)) + ;; Return value. + (cons beg end)))))) +;; ⌘-e: Edit Everything in a separate buffer:5 ends here + +;; [[file:init.org::*⌘-e: Edit Everything in a separate buffer][⌘-e: Edit Everything in a separate buffer:6]] +(use-package language-detection :defer nil) +;; Usage: M-x language-detection-buffer ⇒ Get programming language of current buffer +;; Also, (language-detection-string "select * from t") ;; ⇒ sql + +;; TODO: Push this upstream; https://github.com/andreasjansson/language-detection.el/issues/1 +(cl-defun my/detect-prog-mode (&optional string) + "Guess programming mode of the current buffer, or STRING if it is provided. + +When called interactively, it enables the mode; +from Lisp it just returns the name of the associated mode. + + ;; Example Lisp usage + (call-interactively #'my/detect-prog-mode) + +`language-detection-buffer' returns a string which is not always the name of the +associated major mode; that's what we aim to do here." + (interactive) + + (defvar my/detect-prog-mode/special-names + '((c . c-mode) + (cpp . c++-mode) + (emacslisp . emacs-lisp-mode) + (html . web-mode) ;; I intentionally want to use this alternative. + (matlab . octave-mode) + (shell . shell-script-mode) + (visualbasic . visual-basic-mode) + (xml . sgml-mode)) + "Names in this alist map a language to its mode; all other languages 𝒳 have mode ‘𝒳-mode’ afaik.") + + (let* ((lang (if string (language-detection-string string) (language-detection-buffer))) + (mode (or (cdr (assoc lang my/detect-prog-mode/special-names)) + (intern (format "%s-mode" lang))))) + (if (called-interactively-p 'any) + (progn (call-interactively mode) (message "%s enabled!" mode)) + mode))) +;; ⌘-e: Edit Everything in a separate buffer:6 ends here + +;; [[file:init.org::*⌘-e: Edit Everything in a separate buffer][⌘-e: Edit Everything in a separate buffer:7]] +(advice-add #'org-edit-special :before-until + (lambda (&rest r) + (when (equal 'table-row (car (org-element-at-point))) + (call-interactively #'org-table-edit-field)))) +;; ⌘-e: Edit Everything in a separate buffer:7 ends here + +;; [[file:init.org::*Eldoc for Lisp and Haskell ---documentation in the mini-buffer][Eldoc for Lisp and Haskell ---documentation in the mini-buffer:1]] +(use-package eldoc + :hook (emacs-lisp-mode . turn-on-eldoc-mode) + (lisp-interaction-mode . turn-on-eldoc-mode) + (haskell-mode . turn-on-haskell-doc-mode) + (haskell-mode . turn-on-haskell-indent)) + +;; Slightly shorten eldoc display delay. +(setq eldoc-idle-delay 0.4) ;; Default 0.5 +;; Eldoc for Lisp and Haskell ---documentation in the mini-buffer:1 ends here + +;; [[file:init.org::*Open PDFs in Emacs][Open PDFs in Emacs:1]] + ;; In Org-mode, clicking on PDF should open it in Emacs + ;; Example: [[~/Desktop/stuff-I'm-learning.pdf::12]] ;; Opens the pdf to page 12 + ;; Another Example: docview:~/Desktop/stuff-I'm-learning.pdf::12 + (ignore-errors (add-to-list 'org-file-apps '("\\.pdf\\'" . emacs))) + + ;; Required code to make the above links work as expected. + ;; Source: https://www.reddit.com/r/emacs/comments/re0dx8/how_do_you_link_a_specific_pdf_page_in_org_mode/ + (defun my-org-docview-open-hack (orig-func &rest args) + (let* ((link (car args)) path page) + (string-match "\\(.*?\\)\\(?:::\\([0-9]+\\)\\)?$" link) + (setq path (match-string 1 link)) + (setq page (and (match-beginning 2) + (string-to-number (match-string 2 link)))) + (org-open-file path 1) + (when page + (cond + ((eq major-mode 'pdf-view-mode) + (pdf-view-goto-page page)) + (t + (doc-view-goto-page page)))))) + (advice-add 'org-docview-open :around #'my-org-docview-open-hack) + + ;; Alternatively, there's a dedicated package for this + ;; https://github.com/fuxialexander/org-pdftools/tree/967f48fb5038bba32915ee9da8dc4e8b10ba3376 +;; Open PDFs in Emacs:1 ends here + +;; [[file:init.org::*empv][empv:1]] +(use-package empv ) + +;; Then, M-x empv-play https://invidious.fdn.fr/watch?v=hlTqCmpP5eo, to listen to Dua Ifitiah in the background +;; Or: M-x empv-play https://invidious.fdn.fr/watch?v=9m9yE7qtq5w +;; lol maybe make a hydra(ie playlist) of things I commonly listen to in the background while working. +;; Require: brew install mpv +;; See also: +;; Maybe better? https://github.com/spiderbit/ytdious/tree/941460b51e43ef6764e15e2b9c4af54c3e56115f +;; Maybe better? https://melpa.org/#/yeetube +;; https://github.com/maximus12793/helm-youtube/tree/e7272f1648c7fa836ea5ac1a61770b4931ab4709 +;; https://github.com/isamert/empv.el/tree/1721a581d68f211a7f0104554858ea2afb1723ff +;; +(setq empv-invidious-instance "https://invidious.fdn.fr.com") +;; empv:1 ends here ;; [[file:init.org::*DONE?][DONE?:1]] (find-file "~/.emacs.d/init.org") diff --git a/init.org b/init.org index b3e50f0..c0c11a7 100644 --- a/init.org +++ b/init.org @@ -2,7 +2,7 @@ # # Over 13k line org file, with over 300 source code blocks. -#+title: A Life Configuring Emacs :: TODO :: OPTIMISE FOR BLOGGING! +#+title: A Life Configuring Emacs #+author: Musa Al-hassy #+email: alhassy@gmail.com #+date: 2018-07-25 @@ -13,284 +13,6 @@ #+property: header-args :tangle init.el :comments link :results none #+export_file_name: index -#+begin_src emacs-lisp - (require 'cl-lib) - - (cl-defun maybe-clone (remote &optional local)) - - ;; Prevent undo tree files from polluting your git repo - (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))) -#+end_src - -* RDD - -#+begin_src java -import java.io.IOException; -import java.io.PrintWriter; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.Date; - -;; TODO: Make this an after-the-fact actual advice-add, rather than building this logic into -;; (repl-driven-development ...) ;; i.e., make it the final call in that function -;; -;; Add a user-facing variable “(defvar repl-driven-development/prefix-to-abandon-sessions [C-&])” -;; Users can then set this to ‘nil’ if they don't want this behaviour, or any other prefix. -;; Maybe also make the abaon-sessions function top-level (consuming a keybinding and repl string) so that users can bind their own keys to it. -;; -(advice-add 'repl/jshell\ --enable-preview\ -R\ -ea - :around (lambda (repl &rest args) - (pcase current-prefix-arg - ;; 0 ⇒ Jump to repl [todo, add a keybinding for “C-u 0 C-x C-j” to retrun to original position.] - (0 (switch-to-buffer (--> (buffer-list) (--map (buffer-name it) it) (--filter (s-starts-with? "*REPL/jshell" it) it) car))) - (-1 - ;; restart repl, [then send to repl --does not work since REPLs take a sec to load. That's OK, not a deal-breaker!] - (progn - (--> (buffer-list) (--map (buffer-name it) it) (--filter (s-starts-with? "*REPL/jshell" it) it) car kill-buffer) - (repl-driven-development [C-x C-j] - "jshell --enable-preview -R -ea" ;; enable assertions! - :prompt "jshell>") - (apply repl args))) - ;; if 666, then leave existing repl alone [TODO: Name it, so that we can easily return to it later] - (666 - ;; ⟨1⟩ send region to repl - (apply repl args) - ;; TODO: (-let [session (completing-read "Rebind “C-x C-j” to one of the following named sessions,\nor name the current session for future use and start a new session." '("a" "b"))]) - ;; It's probably rare we want to have named sessions, if someone wants this, they can advise (as done here lol). - ;; ⟨2⟩ For now, just rename the current session /uniquely/ and start a new one - ;; TODO: Actually that might be good enough, since that's how other Emacs processes do it? - (-let [here (current-buffer)] - (switch-to-buffer (--> (buffer-list) (--map (buffer-name it) it) (--filter (s-starts-with? "*REPL/jshell" it) it) car)) - (rename-uniquely) - (-let [new (buffer-name (current-buffer))] - (switch-to-buffer here) - ;; TODO: Maybe make the following part of step ⟨3⟩ by having (repl-driven-development) take a :welcome-msg. - (eros--make-result-overlay (message "Existing REPL session is alive async, kill the buffer when you're done with it. “%s”" new) - :format "﴾ %s ﴿" - :duration 5 ;; output shown for 5 seconds - ;; echo this message one line above the current point position - :where (+ (current-column) (save-excursion (forward-line -1) (point))) - ) - ;; ⟨3⟩ start new session (alongside existing 666 one) - (repl-driven-development [C-x C-j] "jshell --enable-preview -R -ea" :prompt "jshell>")))) - ;; kill all buffers created by this repl; then start a fresh new one. - (999 - (--> (buffer-list) (--map (buffer-name it) it) (--filter (s-starts-with? "*REPL/jshell" it) it) (--map (kill-buffer it) it)) - (repl-driven-development [C-x C-j] "jshell --enable-preview -R -ea" :prompt "jshell>")) - ;; otherwise business as usual - (t (apply repl args))))) - - - -;; remove all advice - (-let [sym 'repl/jshell\ --enable-preview\ -R\ -ea] - (advice-mapc (lambda (advice _props) (advice-remove sym advice)) sym)) - - -(bind-key "C-& C-x C-j" (lambda (beg end) (interactive "r") - (-let [current-prefix-arg 666] - (unless (use-region-p) - (beginning-of-line) - (set-mark-command nil) - (end-of-line) - (setq beg (region-beginning) - end (region-end)) - (pop-mark)) - (funcall-interactively - #'repl/jshell\ --enable-preview\ -R\ -ea - beg end - )))) - -10 (+ (current-column) (save-excursion (forward-line -1) (point))) -20 - -var listener = new ServerSocket(59090); -while (true) { - var socket = listener.accept(); - var sendAfterPrintln = true; - var out = new PrintWriter(socket.getOutputStream(), sendAfterPrintln); - out.println(new Date().toString()); - out.close(); -} - -// Is our server even running? -// (shell-command-to-string "netstat -an | grep 59090") ;; C-x C-e ⇒ LISTENING -// -// Let's invoke our server! -// (shell-command-to-string "nc localhost 59090") ;; C-x C-e ⇒ LISTENING -// -// Without the “out.close()”, this “nc” command gets the date but keeps waiting for more output! - -// Let's write our own “client”: It connects to localhost/59090, prints the response, then exits. -// C-u 666 C-x C-j ⇒ Make existing REPL session async: Start a new session. -var socket = new Socket("localhost", 59090); -var in = new Scanner(socket.getInputStream()); -System.out.println("Server response: " + in.nextLine()); - -// C-u 999 C-x C-j to kill all of these sessions, and start with a fresh Java REPL. -#+end_src - -* COMMENT Init.el misc -#+begin_src emacs-lisp -+ -+;; HACK: The system-packages-ensure pkg is neat and all, but I know I'm using brew. So I'll just define the utility myself, rather than loading an entire pkg. -+;; Moreover, my custom form has the added benefit of only installed packages that are not already installed, thereby foregoing any error messages which crash my init.el -+(defun system-packages-ensure (pkg) -+ (unless (s-contains-p pkg my/installed-packages) -+ (message "Installing ... %s" pkg) -+ ;; to call async shell command without a buffer you need to run: -+ ;; (shell-command "something&" (universal-argument)) -+ (ignore-errors (shell-command-to-string (format "brew install %s &" pkg) (universal-argument))))) - - - -(load (shell-command-to-string "agda-mode locate")) - - - -+;; Prevent undo tree files from polluting your git repo -+(setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))) -+;; That will persist the undo history in ~/.emacs.d/undo instead of locally. -+ -+(set-face-font 'default "Source Code Pro Light 14") -+ -+ -+(use-package eros) -+ -+(require 'eros) -+ -+(defun ordinary-insertion-filter (proc string) -+ (when (buffer-live-p (process-buffer proc)) -+ (with-current-buffer (process-buffer proc) -+ (let ((moving (= (point) (process-mark proc)))) -+ (save-excursion -+ ;; Insert the text, advancing the process marker. -+ (goto-char (process-mark proc)) -+ (insert string) -+ (set-marker (process-mark proc) (point)) -+ ) -+ (if moving (goto-char (process-mark proc)))))) -+ ) -+ -+(cl-defun my/declare-repl (cli keys &key (prompt ">") (prompt* nil) (format " %s")) -+ "CLI is a string denoting a shell command that starts a repl; e.g., `node'. -+ KEYS is the keybinding that will send the selected region to the repl. -+ -+Evaluate a region, if any is selected; otherwise evaluate the current line." -+ (-let* (((cmd . args) (s-split " " cli)) -+ (repl (apply #'start-process "my/REPL" "*REPL*" cmd args))) ;; Identifier "my/REPL" is made unique by start-process. -+ -+ ;; https://stackoverflow.com/questions/4120054/emacs-showing-m-in-a-process-buffer -+ (set-process-coding-system repl 'utf-8-dos y'utf-8-dos) -+ -+ (bind-key keys `(lambda (beg end) (interactive "r") -+ (if current-prefix-arg -+ (progn (switch-to-buffer-other-window (process-buffer ,repl)) -+ (goto-char (point-max))) -+ (unless (use-region-p) -+ (beginning-of-line) -+ (set-mark-command nil) -+ (end-of-line) -+ (setq beg (region-beginning) -+ end (region-end)) -+ (pop-mark)) -+ (process-send-region ,repl beg end) -+ (process-send-string ,repl "\n") -+ ))) -+ (set-process-filter repl `(lambda (process output) -+ (if (or (s-starts-with? " " output) -+ (s-starts-with? ,prompt (s-trim (s-collapse-whitespace output)))) -+ -+ (ordinary-insertion-filter process "\n") -+ -+ ;; (ordinary-insertion-filter process (ansi-color-apply output)) -+ ;; https://www.reddit.com/r/emacs/comments/kbwkca/comment/gfl2196/?utm_source=share&utm_medium=web2x&context=3 -+ -+ (ordinary-insertion-filter process (ansi-color-apply output)) -+ -+ (setq output (s-trim (s-replace (concat "\n" ,prompt) "" (s-replace " -+" "" (s-replace "\r" "" output))))) -+ ;; PROMPT* is like PROMPT, but we deleted everything after the start of the prompt. -+ (when ,prompt* -+ (setq output (s-replace-regexp (concat (regexp-quote ,prompt*) ".*") "" output)) -+ ) -+ ;; that way “C-h e” shows things as well. -+ (let nil ; ((inhibit-message nil)) -+ (message "REPL⇒ %s" (s-collapse-whitespace output))) -+ ;; "Show VALUE as an overlay at the current position of the cursor; return VALUE." ;; output shown for 3 seconds -+ (eros--make-result-overlay (s-collapse-whitespace output) -+ :format ,format -+ :duration 5 ;; output shown for 3 seconds -+ ) -+ ))) -+ repl -+ )) -+ -+(setq repl (my/declare-repl "ts-node -i" "C-x C-t" :prompt* ">" :format " ⇒ %s")) -+ -+(when nil -+ let hi = "Hello" + 1 -+ hi -+ 3 + 3 -+) -+;; (setq repl (my/declare-repl "jshell --enable-preview" "C-x C-j" :prompt "jshell>")) -+(setq repl (my/declare-repl "jshell" "C-x C-j" :prompt "jshell>")) -+ -+ -+ -+ -+ -+ -+(set-face-font 'default "Source Code Pro Light 14") -+ -+;; Use “M-x set-face-font RET default RET”, or... -+;; (set-face-font 'default "Source Code Pro Light14") -+ -+;; See ~2232 fonts -+;; (append (fontset-list) (x-list-fonts "*" nil)) -+ -+ (cl-defun my/toggle-font (&optional (new-font (pop my/fonts))) -+ "Load NEW-FONT, which defaults from ‘my/fonts’. -+ -+When a universal prefix is given, “C-u C-c F”, we load a random -+font from all possible themes. Nice way to learn about more -+fonts (•̀ᴗ•́)و" -+ (interactive) -+ (let* ((all-fonts (append (fontset-list) (x-list-fonts "*" nil))) -+ (font (if current-prefix-arg -+ (nth (random (length all-fonts)) all-fonts) -+ new-font))) -+ (set-face-font 'default font) -+ (message "Font: %s" font))) -+ -+ (global-set-key "\C-c\ F" 'my/toggle-font) -+ -+ ;; Default font; the “ignore-⋯” is for users who may not have the font. -+ (ignore-errors (my/toggle-font "Fantasque Sans Mono 12")) -+ (ignore-errors (my/toggle-font "Source Code Pro Light 14")) -+ -+(unless noninteractive -+ (my/toggle-font "Roboto Mono Light 14") -+ (my/toggle-theme 'solarized-gruvbox-light)) -+ -+(unless noninteractive -+ (load-file (let ((coding-system-for-read 'utf-8)) -+ (shell-command-to-string "/opt/homebrew/Cellar/agda/2.6.2.2_3/bin/agda-mode locate")))) -+ -+;; HACK: The system-packages-ensure pkg is neat and all, but I know I'm using brew. So I'll just define the utility myself, rather than loading an entire pkg. -+;; Moreover, my custom form has the added benefit of only installed packages that are not already installed, thereby foregoing any error messages which crash my init.el -+(defun system-packages-ensure (pkg) -+ (unless (s-contains-p pkg my/installed-packages) -+ (message "Installing ... %s" pkg) -+ ;; to call async shell command without a buffer you need to run: -+ ;; (shell-command "something&" (universal-argument)) -+ (ignore-errors (shell-command-to-string (format "brew install %s &" pkg) (universal-argument))))) -+ -+ -+;; At work, Using Visual Studio Code as your editor -+(shell-command "git config --global core.editor \"code --wait\"") -#+end_src - * Abstract :ignore: :PROPERTIES: :CUSTOM_ID: Abstract @@ -495,15 +217,13 @@ it's first opened/. #+begin_src shell :tangle no # https://github.com/d12frosted/homebrew-emacs-plus $ brew tap d12frosted/emacs-plus -$ brew install emacs-plus@29 --with-xwidgets -$ brew link --overwrite emacs-plus@29 -$ /usr/local/Cellar/emacs-plus@29/29.0.50/bin/emacs-29.0.50 & - -# In ~/.bashrc, or ~/.zshrc, put the following at the end: -alias emacs="/opt/homebrew/Cellar/emacs-plus@29/29.0.90/bin/emacs-29.0.90" +$ brew install emacs-plus@30 --with-xwidgets --with-imagemagick #+end_src - -# alias emacs="/usr/local/Cellar/emacs-plus@29/29.0.50/bin/emacs-29.0.50" + # $ /usr/local/Cellar/emacs-plus@29/29.0.50/bin/emacs-29.0.50 & + # + # In ~/.bashrc, or ~/.zshrc, put the following at the end: + # alias emacs="/opt/homebrew/Cellar/emacs-plus@29/29.0.90/bin/emacs-29.0.90" + # # # If that fails, try this to [[https://github.com/daviderestivo/homebrew-emacs-head#gnu-emacs-27-bottle-or-head][install Emacs:]] @@ -515,14 +235,63 @@ alias emacs="/opt/homebrew/Cellar/emacs-plus@29/29.0.90/bin/emacs-29.0.90" 3. [[https://emacs.stackexchange.com/a/50405/10352][Then]]    [[https://www.emacswiki.org/emacs/EmacsForMacOS#h5o-14][make]]   the command ~emacs~ available via the terminal ---required if doing any melpa development. #+begin_src shell :tangle no -ln -s /usr/local/opt/emacs-head@27/Emacs.app /Applications - -sudo ln -s /usr/local/opt/emacs-head@27/Emacs.app/Contents/MacOS/Emacs /usr/local/bin/emacs +ln -s /opt/homebrew/opt/emacs-plus@30/Emacs.app /Applications #+end_src + # sudo ln -s /usr/local/opt/emacs-head@27/Emacs.app/Contents/MacOS/Emacs /usr/local/bin/emacs + +4. Why ~--with-imagemagick~? + + ;; This lets us change the size of images when shown in Org-mode. + #+begin_src emacs-lisp + (setq org-image-actual-width nil) + #+end_src + + #+begin_org-demo + #+ATTR_HTML: :alt musa in a pink shirt :title The author of this article :align center + #+ATTR_HTML: :width 50% :height 50% + [[~/blog/images/musa_pink.jpg]] + #+end_org-demo + + Note that only the =:width= option is used for in-Org image preview. + +5. Why =--with-xwidgets=? + + We get a full-fledged Internet browser /within/ Emacs. + #+begin_src emacs-lisp + ;; Clicking on a URL, or running M-x browse-url, should open the URL *within* Emacs. + (setq browse-url-browser-function #'xwidget-webkit-browse-url) + + ;; (use-package xwwp) ;; Enhance the Emacs xwidget-webkit browser + #+end_src + + Related: doc:goto-address-mode is useful whenever you have a buffer full of "http..." URLs (e.g.., a JSON file): + It makes them into clickable buttons or via =C-c RET=. + + #+begin_details "How about EAF or nyxt?" + An alternative to xwidget-webkit is EAF, sadly this does not work well with MacOS. -4. Install git: =brew install git= + EAF essentially makes Emacs a window manager that runs other GUI apps ---as such, EAF buffers are not classic Emacs + buffers (and so your favourite text commands are useless). -5. Get my Emacs setup: =rm -rf ~/.emacs.d; git clone + There is also: + | nyxt  ≅  the web running common lisp instead of JS  ≅  an Emacs backed-by common lisp | + #+end_details + + #+begin_details "Tell me more about xwidget-webkit" + I’ve found that the only two applications I regularly have open are Emacs and a browser ---and Emacs has a modern + browser, so might as well use that in Emacs. + - Downsides of Emacs as a browser: Some webpages, such as Slack, try to be an editor and so I'm using a Slack editor + insider a web browser inside an text editor (Emacs). As such, sometimes the lines between editor and browser need to + be explicitly demarcated; e.g., via doc:xwidget-webkit-edit-mode. + - *“xwidget ≈ eXternal widget”* lets us embed GTK widgets inside an Emacs window; e.g., we can insert fancy buttons via + doc:xwidget-insert, and a browser using doc:xwidget-webkit-browse-url. + - For history and info on xwidget, see [[https://github.com/jave/xwidget-emacs][the original patch]]; + See also: https://webkitgtk.org/ + #+end_details + +6. Install git: =brew install git= + +7. Get my Emacs setup: =rm -rf ~/.emacs.d; git clone https://github.com/alhassy/emacs.d.git ~/.emacs.d= Open Emacs and watch download and set up many other things ... ^_^ @@ -546,6 +315,11 @@ If you notice any “file system access” concerns, give Emacs permissions to r your files: General Settings → Security & Privacy → Full Disk Access → ~⌘-M-g~ (to search) then enter =/usr/bin/ruby= ---Emacs is launched via a Ruby script in MacOS. + +-------------------------------------------------------------------------------- + +/See the Prose/Unicode section, we need to manually install the Symbol font for subscripts./ + #+end_details * Why Emacs? :PROPERTIES: @@ -902,7 +676,10 @@ clipboard that allows undoing to any historical state./ (setq undo-tree-visualizer-timestamps t) ;; Show a diff window displaying changes between undo nodes. - (setq undo-tree-visualizer-diff t)) + (setq undo-tree-visualizer-diff t) + + ;; Prevent undo tree files from polluting your git repo + (setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo")))) ;; Execute (undo-tree-visualize) then navigate along the tree to witness ;; changes being made to your file live! @@ -1572,7 +1349,7 @@ to people when I'm promoting Emacs. ⟩ Here are other packages that I want to be installed onto my machine. #+BEGIN_SRC emacs-lisp :tangle init.el ;; Haskell's cool -(use-package haskell-mode :defer t) +(use-package haskell-mode ) ;; Lisp libraries with Haskell-like naming. (use-package dash) ;; “A modern list library for Emacs” @@ -1665,9 +1442,10 @@ us ensure these exist using the ~:ensure-system-package~ keyword extension. Let's obtain the extension. #+BEGIN_SRC emacs-lisp :tangle init.el ;; Auto installing OS system packages - (use-package use-package-ensure-system-package - :config (system-packages-update)) +(use-package system-packages :config (system-packages-update)) +;; Install OS packages using `use-package`. +(use-package use-package-ensure-system-package) ;; Caching the installed pkgs list makes system-package-ensure return nearly immediately for things already installed! (setq my/installed-packages (shell-command-to-string "brew list")) @@ -1968,6 +1746,9 @@ started, toggle follow-mode with ~C-c C-f~ to obtain contextual-awareness; e.g., ;; :defer nil ;; :custom (helm-icons-provider 'all-the-icons) ;; :config (helm-icons-enable)) + +;; When I want to see the TOC of an Org file, show me down to 3 subheadings. +(setq org-imenu-depth 3) #+END_SRC Helm provides generic functions for completions to replace @@ -2254,9 +2035,10 @@ https://alhassy.github.io/org-special-block-extras/ (setq org-confirm-elisp-link-function nil) #+END_SRC -** COMMENT Password-locking files ---“encryption” +** Password-locking files ---“encryption” :Disabled: :PROPERTIES: :CUSTOM_ID: Password-locking-files-encryption +:header-args: :tangle no :END: With the following incantation, we name our files ~𝒳.𝒴.gpg~ where 𝒳 is the file @@ -2299,19 +2081,7 @@ Possibly interesting reads: + [[https://softwareengineering.stackexchange.com/questions/212192/what-are-the-advantages-and-disadvantages-of-cryptographically-signing-commits-a][What are the advantages and disadvantages of cryptographically signing commits and tags in Git? | Software Engineering Stack Exchange]] #+end_details -** all-the-icons -:PROPERTIES: -:CUSTOM_ID: all-the-icons -:END: - -#+begin_src emacs-lisp :tangle init.el - (cl-defun all-the-icons-faicon (icon &rest _) - #("" 0 1 (rear-nonsticky t display (raise -0.24) font-lock-face (:family "FontAwesome" :height 1.2) face (:family "FontAwesome" :height 1.2)))) - -(use-package all-the-icons - :config (all-the-icons-install-fonts 'install-without-asking)) -#+end_src -** TODO COMMENT Hydra: Supply a prefix only once +** Hydra: Supply a prefix only once :PROPERTIES: :CUSTOM_ID: Hydra-Supply-a-prefix-only-once :END: @@ -2366,13 +2136,13 @@ interface by declaraing hydras using [[https://github.com/jerrypnz/major-mode-hy trouble of [[https://github.com/abo-abo/hydra#the-impressive-looking-one][formating docstrings using classic hydra]]. #+begin_src emacs-lisp :tangle init.el -;; TODO Fix me, breaking Github Actions test setup -;; Show hydras overlyaed in the middle of the frame -;; (use-package hydra-posframe -;; :quelpa (hydra-posframe :fetcher git :url -;; "https://github.com/Ladicle/hydra-posframe.git") -;; :hook (after-init . hydra-posframe-mode) -;; :custom (hydra-posframe-border-width 5)) +;; Show hydras overlayed in the middle of the frame +(use-package hydra-posframe + :disabled "TODO Fix me, breaking Github Actions test setup" + :quelpa (hydra-posframe :fetcher git :url + "https://github.com/Ladicle/hydra-posframe.git") + :hook (after-init . hydra-posframe-mode) + :custom (hydra-posframe-border-width 5)) ;; Neato doc strings for hydras (use-package pretty-hydra :defer nil) @@ -2524,7 +2294,7 @@ windows, so we can make a hydra to alleviate the trouble. ("w" enlarge-window-horizontally "widen")) ;; Provides a *visual* way to choose a window to switch to. -;; (use-package switch-window :defer t) +;; (use-package switch-window ) ;; :bind (("C-x o" . switch-window) ;; ("C-x w" . switch-window-then-swap-buffer)) @@ -2550,7276 +2320,7789 @@ Let's save a few precious seconds, (setq-default disabled-command-function nil) #+end_src -** COMMENT An “auto read only” detection mechanism ---when jumping to definitions +** Quickly pop-up a terminal, run a command, close it ---and zsh :PROPERTIES: -:CUSTOM_ID: An-auto-read-only-detection-mechanism-when-jumping-to-definitions +:CUSTOM_ID: Quickly-pop-up-a-terminal-run-a-command-close-it-and-zsh :END: -Files in 3ʳᵈ-party directories should be read-only whenever I open them, when -I'm jumping to definitions (with ~M-.~). +/Pop up a terminal, do some work, then close it using the same command./ #+begin_src emacs-lisp -;; Usage: Press “M-”. “use-package” below and you can accidentally alter the source code! -;; But in this case you likely just wanted to see the 3ʳᵈ-party definition, not alter it. -;; As such, with this advice, the source will not be alterable (unless you toggle read-only mode). -(advice-add #'xref-find-definitions :after - (lambda (&rest _) - (when (--map (s-ends-with? it (f-parent buffer-file-name)) - '("lisp/emacs-lisp" "/lisp" ".emacs.d/elpa/")) - (read-only-mode)))) -#+end_src - -** COMMENT Lisp Programming :relocate: -:PROPERTIES: -:CUSTOM_ID: Lisp-Programming -:END: +(use-package vterm) ;; Shell with a nearly universal compatibility with terminal applications 💝 -*** highlight quoted symbols -:PROPERTIES: -:CUSTOM_ID: highlight-quoted-symbols -:END: +;; "Intelligent" switching to vterm; eg creates it if it's not open, non-intrusive windowing, saves window setup, etc. +(use-package vterm-toggle + :bind* ("C-t" . vterm-toggle)) +#+end_src -#+begin_src emacs-lisp -(use-package highlight-quoted - :defer nil - :config (add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode)) +*** Shell-pop :Disabled:details: -;; If everything worked fine, then “ 'b ” below should be coloured nicely in Emacs Lisp mode. -(when nil - (-let [x 'somevar] - (list x 'b "c" :e))) -#+end_src +[[https://github.com/kyagi/shell-pop-el][Shell-pop]] uses only one key action to work: If the buffer exists, and we're in +it, then hide it; else jump to it; otherwise create it if it doesn't exit. Use +universal arguments, e.g., ~C-u 5 C-t~, to have multiple shells and the same +universal arguments to pop those shells up, but ~C-t~ to pop them away. -* TODO Staying Sane -:PROPERTIES: -:CUSTOM_ID: Staying-Safe -:END: +#+BEGIN_SRC emacs-lisp :tangle no +(use-package shell-pop + :custom + ;; This binding toggles popping up a shell, or moving cursour to the shell pop-up. + (shell-pop-universal-key "C-t") -# See [[https://jeremykun.com/2020/01/14/the-communicative-value-of-using-git-well/][The Communicative Value of Using Git Well]] + ;; Percentage for shell-buffer window size. + (shell-pop-window-size 30) -** COMMENT Dropbox & Mega [Not really in use] -:PROPERTIES: -:CUSTOM_ID: COMMENT-Dropbox-Mega-Not-really-in-use -:END: -Install quick file sharing utility ---[[https://www.dropbox.com/install][Dropbox]]--- as well as a backup -service, [[https://mega.nz/fm][Mega]] ---I use this for my =backups= directory: Keep reading. -# +begin_src shell -# brew install --cask dropbox -# brew install --cask megasync -# +end_src -#+begin_src emacs-lisp -(system-packages-ensure "dropbox") -(system-packages-ensure "megasync") -#+end_src + ;; Position of the popped buffer: top, bottom, left, right, full. + (shell-pop-window-position "bottom") -** Undo-tree: Very Local Version Control -:PROPERTIES: -:CUSTOM_ID: Undo-tree-Very-Local-Version-Control -:END: - doc:undo-tree-visualize, kbd:C-x_u, gives a /visual/ representation of the - current buffer's edit history. -#+BEGIN_SRC emacs-lisp :noweb yes :tangle no - <> + ;; Please use an awesome shell. + (shell-pop-term-shell "/bin/zsh")) #+END_SRC +# There are also a number of hooks for shell-pop. E.g., to unconditionally kill the buffer afterwards. -( We're just showing the ~<>~ from earlier since this is a good -place for such a setup. More importantly, we are /not/ copy-pasting the setup: /It -is written only once; in a single source of truth!/ ) - -#+begin_src emacs-lisp -;; By default C-z is suspend-frame, i.e., minimise, which I seldom use. -(global-set-key (kbd "C-z") - (lambda () (interactive) - (undo-tree-mode) ;; Ensure the mode is on - (undo-tree-visualize))) -#+end_src +*** Oh My Zsh :details: +Now that we have access to quick pop-up for a shell, let's get a pretty and +practical shell: [[https://www.howtogeek.com/362409/what-is-zsh-and-why-should-you-use-it-instead-of-bash/][zsh]] along with the [[https://ohmyz.sh/][Oh My Zsh]] community configurations give us: -** Automatic Backups -:PROPERTIES: -:CUSTOM_ID: Automatic-Backups -:END: +1. ~brew install zsh~ +2. ~sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"~ -By default, Emacs saves backup files ---those ending in =~=--- in the current -directory, thereby cluttering it up. Let's place them in ~~/.emacs.d/backups~, in -case we need to look for a backup; moreover, let's keep old versions since -there's disk space to go around ---what am I going to do with 500gigs when nearly -all my ‘software’ is textfiles interpreted within Emacs 😼 + This installs everything ^_^ #+BEGIN_SRC emacs-lisp -;; New location for backups. -(setq backup-directory-alist '(("." . "~/.emacs.d/backups"))) - -;; Silently delete execess backup versions -(setq delete-old-versions t) +;; Be default, Emacs please use zsh +;; E.g., M-x shell +(unless noninteractive (setq shell-file-name "/bin/zsh")) +#+END_SRC -;; Only keep the last 1000 backups of a file. -(setq kept-old-versions 1000) - -;; Even version controlled files get to be backed up. -(setq vc-make-backup-files t) - -;; Use version numbers for backup files. -(setq version-control t) -#+END_SRC +Out of the box, zsh comes with ++ git support; the left side indicates which branch we're on and + whether the repo is dirty, ✗. ++ Recursive path expansion; e.g., ~/u/lo/b TAB~ expands to ~/usr/local/bin/~ ++ Over [[https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins#apache2-macports][250+ Plugins]] and [[https://github.com/ohmyzsh/ohmyzsh/wiki/Themes][125+ Themes]] that are enabled by simply + mentioning their name in the ~.zshrc~ file. -Why backups? Sometimes I may forget to submit a file, or edit, to my -version control system, and it'd be nice to be able to see a local -automatic backup. Whenever ‘I need space,’ then I simply empty -the backup directory, if ever. That the backups are numbered is so sweet ^_^ +The defaults have been good enough for me, for now ---as all else is achieved +via Emacs ;-) -Like package installations, my backups are not kept in any version control -system, like git; only locally. +*** tldr -Finally, let's not create ~.#~ files (crashes 'npm') nor bother confirming killing processes. -#+begin_src emacs-lisp -(setq confirm-kill-processes nil - create-lockfiles nil) -#+end_src +Also, there's the [[https://tldr.sh/][tldr]] tool which aims to be like terse manuals for +commandline-tools in the style of practical example uses cases: ~tldr 𝒳~ yields a +number of ways you'd actually use 𝒳. ( In Emacs, [[kbd:C-t tldr 𝒳 ⟨return⟩]]. ) +#+BEGIN_SRC emacs-lisp +(system-packages-ensure "tldr") +#+END_SRC -*** What changed? ---Walking through backups +* Literate Programming :PROPERTIES: -:CUSTOM_ID: What-changed +:CUSTOM_ID: Literate-Programming :END: -Let's use an elementary diff system for backups: ~backup-walker~ essentially makes -all our backups behave as if they were (implicitly) version controlled. - -#+BEGIN_SRC emacs-lisp -(use-package backup-walker - :commands backup-walker-start) -#+END_SRC +Org-mode lets us run chunks of code anywhere, then feed their outputs to other +chunks of code in /possibly different/ programming languages: Org is a +meta-(programming language). -In a buffer that corresponds to a file, invoke doc:backup-walker-start to see a -visual diff of changes /between/ versions; then ~n~ and ~p~ to move between diffs. By -default, you see the changes ‘backwards’: Red means delete these things to get -to the older version; i.e., the red ‘-’ are newer items. +Importantly, this means we can write text and whenever we need the result of +some computation, we can place it there and then and only request its result +appear in PDF/HTML export. The result is a single document. -There is also doc:diff-backup for comparing a file with its backup. +( There is the [[https://github.com/minad/org-modern][org-modern]] package, which provides a modern look-and-feel: It +makes Org look less like a markup and more like a word editor. Nice stuff. ) -# To create backups explicitly use save-buffer (C-x C-s) with prefix arguments. -# -# =diff-backup= compares a file with its backup or vice versa. -# But there is no function to restore backup files. +** High Speed Literate Programming +:PROPERTIES: +:CUSTOM_ID: High-Speed-Literate-Programming +:END: -*** Save ≈ Backup +*** Manipulating Sections :PROPERTIES: -:CUSTOM_ID: Save-Backup +:CUSTOM_ID: Manipulating-Sections :END: -Emacs only makes a backup the very first time a buffer is saved; I'd prefer -Emacs makes backups everytime I save! ---If I saved, that means I'm at an -important checkpoint, so please check what I have so far as a backup! #+BEGIN_SRC emacs-lisp -;; Make Emacs backup everytime I save +(setq org-use-speed-commands t) +#+END_SRC -(defun my/force-backup-of-buffer () - "Lie to Emacs, telling it the curent buffer has yet to be backed up." - (setq buffer-backed-up nil)) +This enables the [[http://notesyoujustmightwanttosave.blogspot.com/2011/12/org-speed-keys.html][Org Speed Keys]] so that when the cursor is at the beginning of a +headline, we can perform fast manipulation & navigation using the standard Emacs +movement controls, such as: ++ [[kbd:#]] toggle ~COMMENT~-ing for an org-header. ++ [[kbd:s]] toggles [[doc:narrow-to-region][“narrowing”]] to a subtree; i.e., hide the rest of the document. -(add-hook 'before-save-hook 'my/force-backup-of-buffer) + If you narrow to a subtree then any export, kbd:C-c_C-e, will joyously only + consider the narrowed detail. ++ [[kbd:u]] for going to upwards to parent heading + * [[kbd:i]] insert a new same-level heading below current heading. ++ [[kbd:c]] for cycling structure below current heading, or ~C~ for cycling global structure. ++ [[kbd:w]] refile current heading; options list pops-up to select which heading to move + it to. Neato! + - [[kbd:g]] to go to another heading, without refiling anything. + #+begin_src emacs-lisp +;; When refiling, only show me top level headings [Default]. Sometimes 2 is useful. +;; When I'm refiling my TODOS, then give me all the freedom. +(setq org-refile-targets '((nil :maxlevel . 1) + (org-agenda-files :maxlevel . 9))) -;; [Default settings] -;; Autosave when idle for 30sec or 300 input events performed -(setq auto-save-timeout 30 - auto-save-interval 300) -#+END_SRC -# See the name of the latest backup, which is appeneded by a number for each save ;-) -# (file-newest-backup "~/.emacs.d/init.org") +;; Maybe I want to refile into a new heading; confirm with me. +(setq org-refile-allow-creating-parent-nodes 'confirm) -It is intestesting to note that the above snippet could be modified to [[https://stackoverflow.com/a/6918217/3550444][make our -own backup system]], were Emacs lacked one, by having our function simply save -copies of our file ---on each save--- where the filename is augmented with a -timestamp. +;; Use full outline paths for refile targets +;; When refiling, using Helm, show me the hierarchy paths +(setq org-outline-path-complete-in-steps nil) +(setq org-refile-use-outline-path 'file-path) + #+end_src ++ kbd:n/p for next/previous /visible/ heading. ++ kbd:f/b for jumping forward/backward to the next/previous /same-level/ heading. ++ kbd:D/U move a heading down/up. ++ kbd:L/R recursively promote (move leftwards) or demote (more rightwards) a heading. ++ kbd:I/O clock In/Out to the task defined by the current heading. + - Keep track of your work times! + - [[kbd:v]] view agenda. ++ kbd:t/,/:/e to add a TODO state, priority level, tag, or effort estimate + * kbd:1/2/3 to mark a heading with priority, highest to lowest. ++ [[kbd:^]] sort children of current subtree; brings up a list of sorting options. ++ kbd:k/@/a to kill or mark or archive the current subtree ++ [[kbd:o]] to open a link mentioned in the subtree then go to the link; a pop-up of + links appears. -** TODO   =magit= ---Emacs' porcelain interface to git +We can add our own speed keys by altering the doc:org-speed-commands association +list variable; e.g., +#+begin_src emacs-lisp +;; TODO FIXME Crashes upon startup. +(when nil (add-to-list 'org-speed-commands (cons "P" #'org-set-property))) +;; Use ‘:’ and ‘e’ to set tags and effort, respectively. +#+end_src + +# To see the commands available, execute ~M-x org-speed-command-help~. +| ⇒ Moreover, [[kbd:?]] to see a complete list of keys available. ⇐ | + +*** Seamless Navigation Between Source Blocks :PROPERTIES: -:CUSTOM_ID: magit-Emacs'-porcelain-interface-to-gitq +:CUSTOM_ID: Seamless-Navigation-Between-Source-Blocks :END: -*** Intro :ignore: +The “super key” ---aka the command or windows key--- can be used to jump to the +previous, next, or toggle editing org-mode source blocks. +#+begin_src emacs-lisp +;; Overriding keys for printing buffer, duplicating gui frame, and isearch-yank-kill. +;; +(require 'org) +(use-package emacs + :bind (:map org-mode-map + ("s-p" . org-babel-previous-src-block) + ("s-n" . org-babel-next-src-block) + ("s-e" . org-edit-special) + :map org-src-mode-map + ("s-e" . org-edit-src-exit))) +#+end_src -Let's setup an Emacs ‘porcelain’ interface to git ---it makes working with -version control tremendously convenient. -# Moreover, I add a little pop-up so that I don't forget to commit often! +# Interestingly, ~s-l~ is “goto line”. -(Personal reminder: If using 2FA [two factor authentication], then when you do -git operations, such as ~git push~, [[https://webkul.com/blog/github-push-with-two-factor-authentication/][you must use your PAT]] [personal access token] -/instead/ of your password! Also: Install [[https://github.com/sindresorhus/refined-github][refined-github: Browser extension that -simplifies the GitHub interface and adds useful features]]!) +Note that we could have bound kbd:⌘+e to doc:org-edit-src-code / +doc:org-edit-src-exit, but instead chose the more general doc:org-edit-special +since, well, look at the tooltip documentation: This allows us to use kbd:⌘+e to +‘e’dit all kinds of Org entities ---including footnotes and export blocks. ( +Footnotes can be quickly produced with doc:org-footnote-new. ) -#+BEGIN_SRC emacs-lisp -;; Bottom of Emacs will show what branch you're on -;; and whether the local file is modified or not. -(use-package magit - :init (require 'magit-files) - :bind (("C-c M-g" . magit-file-dispatch)) - :custom ;; Do not ask about this variable when cloning. - (magit-clone-set-remote.pushDefault t)) -#+END_SRC -# :config (global-set-key (kbd "C-x g") 'magit-status) +*** Modifying [[kbd:⟨return⟩]] +:PROPERTIES: +:CUSTOM_ID: Modifying-return +:END: -Why use ~magit~ as the interface to the git version control system? In a ~magit~ -buffer nearly everything can be acted upon: Press =return=, or =space=, to see -details and =tab= to see children items, usually. ++ [[kbd:C-⟨return⟩]] , [[kbd:C-S-⟨return⟩]] make a new heading where the latter marks it as a ~TODO~. ++ By default [[kbd:M-⟨return⟩]] makes it easy to work with existing list items, headings, + tables, etc by creating a new item, heading, etc. -+ kbd:C-x_g, doc:magit-status, gives you a nice buffer with an overview - of the Git repo that you're buffer is currently visiting. -+ kbd:C-c_M-g, doc:magit-file-dispatch, lets you invoke Git actions on the - current file directly; e.g., following up with [[kbd:b]]lame, [[kbd:l]]og, [[kbd:d]]iff, - [[kbd:s]]tage, or [[kbd:c]]ommit the current file. +Usually we want a newline then we indent, let's make that the default. + #+BEGIN_SRC emacs-lisp +(add-hook 'org-mode-hook '(lambda () + (local-set-key (kbd "") 'org-return-indent)) + (local-set-key (kbd "C-M-") 'electric-indent-just-newline)) +#+END_SRC - For ease, above, we have also bound this to kbd:C-c_g ---reminiscent of kbd:C-x_g :smile: + Notice that I've also added another kind of return, for when I want to + break-out of the indentation approach and start working at the beginning of + the line. - #+begin_src emacs-lisp -;; When we invoke magit-status, show green/red the altered lines, with extra -;; green/red on the subparts of a line that got alerted. -(system-packages-ensure "git-delta") -(use-package magit-delta - :hook (magit-mode . magit-delta-mode)) +In summary: +| key | method | behaviour | +|------------------+---------------------------------------------+-----------------------------------| +| [[kbd:⟨return⟩]] | doc:org-return-indent | Newline with indentation | +| [[kbd:M-⟨return⟩]] | doc:org-meta-return | Newline with new org item | +| [[kbd:C-M-⟨return⟩]] | doc:electric-indent-just-newline | Newline, cursor at start | +| [[kbd:C-⟨return⟩]] | doc:org-insert-heading-respect-content | New heading /after/ current content | +| [[kbd:C-S-⟨return⟩]] | doc:org-insert-todo-heading-respect-content | Ditto, but with a ~TODO~ marker | -;; Don't forget to copy/paste the delta config into the global ~/.gitconfig file. -;; Copy/paste this: https://github.com/dandavison/delta#get-started - #+end_src +*** COMMENT   ~C-a,e,k~ and Yanking of sections :Does_not_work: +:PROPERTIES: +:CUSTOM_ID: C-a-e-k-and-Yanking-of-sections +:END: -+ Blame, doc:magit-blame, is super nice: The buffer gets annotations for each chunk - of text, regarding who authoured it, when, and their commit title. - Then [[kbd:q]] to quit the blame. +On an org-heading, kbd:C-a goes to /after/ the star, heading markers. To use +speed keys, run kbd:C-a_C-a to get to the star markers. - Likewise, doc:magit-log-buffer-file is super neat! +kbd:C-e goes to the end of the heading, not including the tags. +#+begin_src emacs-lisp +(setq org-special-ctrl-a/e t) +#+end_src -#+begin_details Super Simple ‘magit’ Mini-tutorial -link-here:Super-Simple-‘magit’-Mini-tutorial -Below is my personal quick guide to working with magit ---for a full tutorial -see [[http://jr0cket.co.uk/2012/12/driving-git-with-emacs-pure-magic-with.html.html][jr0cket's blog]]. +kbd:C-k no longer removes tags, if activated in the middle of a heading's name. +#+begin_src emacs-lisp +(setq org-special-ctrl-k t) ;; MA: Does not work …! +#+end_src -+ ~dired~ :: See the contents of a particular directory. +When you yank a subtree and paste it alongside a subtree of depth /‘d’/, then the +yanked tree's depth is adjusted to become depth /‘d’/ as well. If you don't want +this, then refile instead of copy-pasting. +#+begin_src emacs-lisp +(setq org-yank-adjusted-subtrees t) +#+end_src +** Executing code from ~src~ blocks +:PROPERTIES: +:CUSTOM_ID: Executing-code-from-src-blocks +:END: -+ ~magit-init~ :: Put a project under version control. - The mini-buffer will prompt you for the top level folder version. - A ~.git~ folder will be created there. - -+ ~magit-status~ , ~C-x g~ :: See status in another buffer. - Press ~?~ to see options, including: - - g :: Refresh the status buffer. - # The status buffer may be refereshed using ~g~, and all magit buffers by ~G~. - - TAB :: See collapsed items, such as what text has been changed. - - ~q~ :: Quit magit, or go to previous magit screen. - - ~s~ :: Stage, i.e., add, a file to version control. - Add all untracked files by selecting the /Untracked files/ title. +For example, to execute a shell command in Emacs, write a ~src~ with a shell +command, then ~C-c c-c~ to see the results. Emacs will generally query you to +ensure you're confident about executing the (possibly dangerous) code block; +let's stop that: +#+BEGIN_SRC emacs-lisp :tangle init.el +;; Seamless use of babel: No confirmation upon execution. +;; Downside: Could accidentally evaluate harmful code. +(setq org-confirm-babel-evaluate nil) - [[https://softwareengineering.stackexchange.com/a/119807/185815][The staging area is akin to a pet store; commiting is taking the pet home.]] +;; Never evaluate code blocks upon export and replace results when evaluation does occur. +;; For a particular language 𝑳, alter ‘org-babel-default-header-args:𝑳’. +(setq org-babel-default-header-args + '((:results . "replace") + (:session . "none") + (:exports . "both") + (:cache . "no") + (:noweb . "no") + (:hlines . "no") + (:tangle . "no") + (:eval . "never-export"))) +#+END_SRC - - ~k~ :: Kill, i.e., delete a file locally. - - ~K~ :: This' ~(magit-file-untrack)~ which does ~git rm --cached~. - - ~i~ :: Add a file to the project ~.gitignore~ file. Nice stuff =) - - ~u~ :: Unstage a specfif staged change highlighed by cursor. - ~C-u s~ stages everything --tracked or not. - - ~c~ :: Commit a change. - * A new buffer for the commit message appears, you write it then - commit with ~C-c C-c~ or otherwise cancel with ~C-c C-k~. - These commands are mentioned to you in the minibuffer when you go to commit. - * You can provide a commit to /each/ altered chunk of text! - This is super neat, you make a series of local such commits rather - than one nebulous global commit for the file. The ~magit~ interface - makes this far more accessible than a standard terminal approach! - * You can look at the unstaged changes, select a /region/, using ~C-SPC~ as usual, - and commit only that if you want! - * When looking over a commit, ~M-p/n~ to efficiently go to previous or next altered sections. - * Amend a commit by pressing ~a~ on ~HEAD~. - - ~d~ :: Show differences, another ~d~ or another option. - - This is magit! Each hunk can be acted upon; e.g., ~s~ or ~c~ or ~k~ ;-) - - ~v~ :: Revert a commit. - - ~x~ :: Undo last commit. Tantamount to ~git reset HEAD~~ when cursor is on most recent - commit; otherwise resets to whatever commit is under the cursor. - - ~l~ :: Show the log, another ~l~ for current branch; other options will be displayed. - - Here ~space~ shows details in another buffer while cursour remains in current - buffer and, moreover, continuing to press ~space~ scrolls through the other buffer! - Neato. - - ~P~ :: Push. - - ~F~ :: Pull. - - ~:~ :: Execute a raw git command; e.g., enter ~whatchanged~. +# A worked out example can be obtained as follows: ~> ~/dotfiles/.emacs") - (find-file "~/dotfiles/.emacs")) -#+END_EXAMPLE -:End: -# In doubt, execute ~C-h e~ to jump to the ~*Messages*~ buffer. -#+end_details +More languages can be added using doc:add-to-list. -*** TODO COMMENT Credentials: I am who I am +** Executing all =#+name: startup-code= for local configurations :PROPERTIES: -:CUSTOM_ID: Credentials-I-am-who-I-am +:CUSTOM_ID: Executing-all-name-startup-code-for-local-configurations :END: -First, let's setup our git credentials. -#+BEGIN_SRC emacs-lisp -;; See here for a short & useful tutorial: -;; https://alvinalexander.com/git/git-show-change-username-email-address -(when (equal "" (shell-command-to-string "git config user.email ")) - (shell-command (format "git config --global user.name \"%s\"" user-full-name)) - (shell-command (format "git config --global user.email \"%s\"" user-mail-address))) -;; Also need to customise email routes per organization -;; https://docs.github.com/en/github/managing-subscriptions-and-notifications-on-github/configuring-notifications#customizing-email-routes-per-organization -(ignore-error (unless my/personal-machine? - (shell-command (format "git config --global user.email \"%s\"" work/email)))) -#+END_SRC +Sometimes my Org-files contain configurations that are local to the file, +so I name all such =src= blocks =#+name: startup-code= and place =# -*- eval: (my/execute-startup-blocks) -*-= at the top of the file so that such +blocks are evaluated when the file opens up. +- The =-*- ... -*-= notation is for making local configurations. +- Use =M-x add-file-local-variable-prop-line= to have them inserted interactively. +#+begin_src emacs-lisp +(defun my/execute-startup-blocks () + "Execute all startup blocks, those named ‘startup-code’. -If we ever need to use Git in the terminal, it should be done with Emacs as the -underlying editor +I could not use ORG-BABEL-GOTO-NAMED-SRC-BLOCK since it only goes +to the first source block with the given name, whereas I'd like to +visit all blocks with such a name." + (interactive) + (save-excursion + (goto-char 0) + (while (ignore-errors (re-search-forward "^\\#\\+name: startup-code")) + (org-babel-execute-src-block)))) +#+end_src + +The following setup enables this feature in a safe fashion ---e.g., we do not +want to avoid evaluating a random person's potentially dangerous code when we +only want to look at it. #+BEGIN_SRC emacs-lisp -(shell-command "git config --global core.editor 'emacsclient -t -a=\\\"\\\"'") +;; Please ask me on a file by file basis whether its local variables are ‘safe’ +;; or not. Use ‘!’ to mark them as permanently ‘safe’ to avoid being queried +;; again for the same file. +(setq enable-local-variables t) #+END_SRC -This will start a daemon if there is not already one running ---The ~-a~ option--- -and opens a new Emacs frame on the current terminal ---The ~-t~ option. -*** Encouraging useful commit messages +I have been using a combination of =(org-babel-goto-named-src-block ⋯)= in +multi-line local-variable declarations ---=M-x add-file-local-variable-prop=--- +for a while in many files using a dedicated =* footer :noexport:= section, but +this new approach frees from having such sections and instead to having a single +line at the top of the file. Moreover, being at the top of the file, such a line +is a nice *[[green:‘in your face’]]* reminder that there is local configuration that +should have been loaded. +# - E.g., this init file has local configuration for making the corresponding +# =init.el= file and generating the =README.org= file. +** Snippets ---Template Expansion & Completion :PROPERTIES: -:CUSTOM_ID: Encouraging-useful-commit-messages +:CUSTOM_ID: Snippets-Template-Expansion :END: -Let's try our best to have a [[https://chris.beams.io/posts/git-commit/][useful & consistent commit log]]: -#+begin_src emacs-lisp -(defun my/git-commit-reminder () - (insert "\n\n# The commit subject line ought to finish the phrase: -# “If applied, this commit will ⟪your subject line here⟫.” ") - (beginning-of-buffer)) -(add-hook 'git-commit-setup-hook 'my/git-commit-reminder) -#+end_src +It is common that there is a sequence of text that we tend to repeat +often, possibly with a name or some other parameter altered. +Such a ‘snippet’ could be written once then provided by a simple +Lisp insert command with the parameters being queried. Luckily, others +have written such pleasant utilities. -Super neat stuff! +Besides snippets, there are words that we may want to repeat often but it can be +tedious to write them out in full. As such, we employ *[[green: word completion]]*; +which we also use to expand our snippets. +# For instance, I knew someone who writes ‘U’ all over the place +# since the word “universe” is too long to write +# and Emacs doesn't come with word completion. -*** Maybe clone ... everything? +*** Word Completion and Documentation Pop-ups :PROPERTIES: -:CUSTOM_ID: Maybe-clone-everything +:CUSTOM_ID: Word-Completion :END: -Below are the git repos I'd like to clone ---along with a function to do so -quickly. +Let's enable [[https://company-mode.github.io/][“complete anything” mode]] ---it ought to start in half a second and +only need two characters to get going, which means word suggestions are provided +and so I need only type partial words then tab to get the full word! + #+begin_src emacs-lisp -;; Clone git repo from clipboard -(cl-defun maybe-clone (remote &optional local) - "Clone a REMOTE repository [from clipboard] if the LOCAL directory does not exist. +(use-package company -If called interactively, clone URL in clipboard into ~/Downloads then open in dired. + :config + (global-company-mode 1) + (setq ;; Only 2 letters required for completion to activate. + company-minimum-prefix-length 2 -Yields ‘repo-already-exists’ when no cloning transpires, otherwise yields ‘cloned-repo’. + ;; Search other buffers for compleition candidates + company-dabbrev-other-buffers t + company-dabbrev-code-other-buffers t -LOCAL is optional and defaults to the base name; e.g., -if REMOTE is https://github.com/X/Y then LOCAL becomes ∼/Y." - (interactive "P") + ;; Show candidates according to importance, then case, then in-buffer frequency + company-transformers '(company-sort-by-backend-importance + company-sort-prefer-same-case-prefix + company-sort-by-occurrence) - (when (interactive-p) - (setq remote (substring-no-properties (current-kill 0))) - (cl-assert (string-match-p "^\\(http\\|https\\|ssh\\)://" remote) nil "No URL in clipboard")) + ;; Flushright any annotations for a compleition; + ;; e.g., the description of what a snippet template word expands into. + company-tooltip-align-annotations t - (unless local - (setq local (concat "~/" (if (interactive-p) "Downloads/" "") (file-name-base remote)))) + ;; Allow (lengthy) numbers to be eligible for completion. + company-complete-number t - ;; (require 'magit-repos) ;; Gets us the magit-repository-directories variable. - ;; (add-to-list 'magit-repository-directories `(,local . 0)) + ;; M-⟪num⟫ to select an option according to its number. + company-show-numbers t - (if (file-directory-p local) - 'repo-already-exists - (shell-command (concat "git clone " remote " " local)) - (dired local) - 'cloned-repo)) + ;; Show 10 items in a tooltip; scrollbar otherwise or C-s ^_^ + company-tooltip-limit 10 + ;; Edge of the completion list cycles around. + company-selection-wrap-around t -(maybe-clone "https://github.com/alhassy/emacs.d" "~/.emacs.d") -(maybe-clone "https://github.com/alhassy/alhassy.github.io" "~/blog") -(maybe-clone "https://github.com/alhassy/holy-books") -#+end_src + ;; Do not downcase completions by default. + company-dabbrev-downcase nil -#+begin_details Many more repos to clone -#+begin_src emacs-lisp + ;; Even if I write something with the ‘wrong’ case, + ;; provide the ‘correct’ casing. + company-dabbrev-ignore-case nil -(maybe-clone "https://github.com/alhassy/melpa") -(maybe-clone "https://github.com/alhassy/org-special-block-extras") + ;; Immediately activate completion. + company-idle-delay 0) + ;; Use C-/ to manually start company mode at point. C-/ is used by undo-tree. + ;; Override all minor modes that use C-/; bind-key* is discussed below. + (bind-key* "C-/" #'company-manual-begin) -;; (maybe-clone "https://github.com/alhassy/next-700-module-systems-proposal.git" "~/thesis-proposal") -;; (maybe-clone "https://github.com/JacquesCarette/MathScheme") -;; (maybe-clone "https://github.com/alhassy/gentle-intro-to-reflection" "~/reflection/") -;; (maybe-clone "https://github.com/alhassy/org-agda-mode") -;; (maybe-clone "https://github.com/JacquesCarette/TheoriesAndDataStructures") -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/RATH/RATH-Agda" "~/RATH-Agda") -;; (maybe-clone "https://github.com/alhassy/MyUnicodeSymbols") ;; Deleted? + ;; Bindings when the company list is active. + :bind (:map company-active-map + ("C-d" . company-show-doc-buffer) ;; In new temp buffer + ("" . company-complete-selection) + ;; Use C-n,p for navigation in addition to M-n,p + ("C-n" . (lambda () (interactive) (company-complete-common-or-cycle 1))) + ("C-p" . (lambda () (interactive) (company-complete-common-or-cycle -1))))) -(maybe-clone "https://github.com/alhassy/islam") -(maybe-clone "https://github.com/alhassy/CheatSheet") -(maybe-clone "https://github.com/alhassy/ElispCheatSheet") -;; (maybe-clone "https://github.com/alhassy/CatsCheatSheet") -;; (maybe-clone "https://github.com/alhassy/OCamlCheatSheet") -;; (maybe-clone "https://github.com/alhassy/AgdaCheatSheet") -(maybe-clone "https://github.com/alhassy/RubyCheatSheet") -;; (maybe-clone "https://github.com/alhassy/PrologCheatSheet") -;; (maybe-clone "https://github.com/alhassy/FSharpCheatSheet") +;; It's so fast that we don't need a key-binding to start it! +#+end_src +Note that ~M-/~ goes through a sequence of completions ---and ~C-/~ manually begins +company mode at point. Besides the arrow keys, we can also use ~M-~ with ~n, p~ to +navigate the options /or/ use ~C-s~ to search the list of suggestions. ++ Company backends are available as separate packages. ++ Note that [[https://github.com/company-mode/company-mode/issues/360][by default]] company mode does not support completion for phrases + containing hyphens ---this can be altered, if desired. -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/armstmp/cs3mi3.git" "~/3mi3") -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/CAS781" "~/cas781") ;; cat adventures -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/carette/cs3fp3.git" "~/3fp3") -;; (maybe-clone "https://github.com/alhassy/interactive-way-to-c") -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-distribution.git" "~/3ea3/assignment-distribution") -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/notes.git" "~/3ea3/notes") -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-development.git" "~/3ea3/assignment-development") -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/kandeeps.git" "~/3ea3/sujan") -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/horsmane.git" "~/3ea3/emily") -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/anderj12.git" "~/3ea3/jacob") -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/3EA3.git" "~/3ea3/_2018") -;; (maybe-clone "https://gitlab.cas.mcmaster.ca/2DM3/LectureNotes.git" "~/2dm3") -#+end_src -#+end_details +Let's add a test for some basic features of completition: +#+begin_src emacs-lisp :tangle init-test.el +(ert-deftest company-works-as-expected-in-text-mode () + :tags '(company) + (switch-to-buffer "*TESTING COMPANY MODE ~ Text*") -This doc:maybe-clone utility has genuinely -made it easier for me to learn about new projects and codebases from Github: -I type it in with the repo's address, then kbd:C-x_C-e ---doc:eval-last-sexp--- -and then I can view it in my beloved Emacs ~(─‿‿─)~. + ;; Ensure we have a clear buffer, and enter some text. (Namely, Python code). + (erase-buffer) + (insert "\n def first(x): pass") + (insert "\n def fierce(a, b): pass") -Moreover, this handy tool makes it so that you can -list your Git repositories with doc:magit-list-repositories: -It marks modified repos as “red:dirty”. + ;; Completion anything mode is enabled by default. + (should company-mode) -It may be useful to know that ~(magit-anything-modified-p t file)~ can be used to -check if ~file~ has been modified (doc:magit-anything-modified-p), whereas -~(magit-status repo)~ checks the status of a repository (doc:magit-status). -# (magit-anything-modified-p t "~/ElispCheatSheet/CheatSheet.org") -# (magit-status "~/ElispCheatSheet") + ;; There are 2 completion candidates: The two we wrote. + (insert "\n fi") + (company-manual-begin) ;; i.e., C-/ + (should (equal company-candidates '("fierce" "first"))) -*** Gotta love that time machine -:PROPERTIES: -:CUSTOM_ID: Gotta-love-that-time-machine -:END: + ;; [fi ↦ fierce] Default option is the most recently matching written word. + (insert "\n fi") + (execute-kbd-macro (kbd "C-/ ")) + (should (looking-back "fierce")) -Finally, one of the main points for using version control is to have access to -historic versions of a file. The following utility allows us to -kbd:M-x_git-timemachine on a file and use kbd:p/n/g/q to look at previous, next, -goto arbitrary historic versions, or quit. -#+BEGIN_SRC emacs-lisp -(use-package git-timemachine :defer t) -#+END_SRC -If we want to roll back to a previous version, we just doc:write-file or -kbd:C-x_C-s as usual! The power of text! + ;; [fi ↦ first] We can use M-2 to select the candidate “first” + (insert "\n fi") + (execute-kbd-macro (kbd "C-/ M-2")) + (should (looking-back "first")) --------------------------------------------------------------------------------- + (kill-buffer)) -doc:vc-annotate is also very useful to go through history and work out when -things went wrong. +;; Let's enter Python mode and see how things change -*** Jump to a (ma)git repository with ~C-u C-x g~ -:PROPERTIES: -:CUSTOM_ID: Jump-to-a-ma-git-repository-with-C-u-C-x-g -:END: +(ert-deftest company-shows-keywords-alongside-completions-alphabetically () + :tags '(company) + (switch-to-buffer "*TESTING COMPANY MODE ~ Python*") + (python-mode) -#+begin_src emacs-lisp -;; Jump to a (ma)git repository with C-u C-x g. -;; -;; To get a selection of repositories (that have been visited at least once), -;; call with “C-u M-x magit-status” or “C-u C-x g”; use “C-u C-u C-x g” to -;; manually enter a path to a repository. -;; -;; We use projectile's record of known projects, and keep only projects with -;; .git directory. -(with-eval-after-load 'projectile - (setq magit-repository-directories - (thread-last (projectile-relevant-known-projects) - (--filter (unless (file-remote-p it) - (file-directory-p (concat it "/.git/")))) - (--map (list (substring it 0 -1) 0))))) + ;; Ensure we have a clear buffer, and enter some text. (Namely, Python code). + (erase-buffer) + (insert "\n def first(x): pass") + (insert "\n def fierce(a, b): pass") -;; Follow-up utility -(defun my/update-repos () - "Update (git checkout main & pull) recently visited repositories." - (interactive) - (cl-loop for (repo _depth) in magit-repository-directories - ;; Is it “main” or “master” - for trunk = (s-trim (shell-command-to-string (format "cd %s; git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'" repo))) - do (message (format "🤖 %s ∷ Checking out & pulling main" repo)) - (shell-command (format "cd %s; git checkout %s; git pull" repo trunk))) - (message "🥳 Happy coding!")) -#+end_src + ;; There are 3 completion candidates: The two we wrote, & the third being a Python keyword. + (insert "\n fi") + (company-manual-begin) + (should (equal company-candidates '("fierce" "first" #("finally" 0 7 (company-backend company-keywords))))) -** COMMENT Github /within/ Emacs -:PROPERTIES: -:CUSTOM_ID: Github-within-Emacs -:END: + ;; Candidates are shown alphabetically: M-2 yields “finally”. + (execute-kbd-macro (kbd "C-g C-/ M-2")) ;; Quit and restart the completion, to get to starting position, then M-2. + (should (looking-back "finally")) -We can work with Github/Gitlab/etc from the comfort of Emacs Magit using the [[https://github.com/magit/forge][forge]] package. -+ In particular, it adds two new “topics” to the Magit status page: =Issues= and =Pull Requests=. - - We can tab on these to see their “posts” ---the contributions to the conversations and /new/ commits. - - [[https://magit.vc/manual/forge/Editing-Topics-and-Posts.html][Here]] is a list of actions that can be done on topics and posts. + (kill-buffer)) + +#+end_src +We do ~M-: (ert '(tag company))~ to run these specific tests; better would be to +call these (and all) tests with a nifty name: #+begin_src emacs-lisp -(use-package forge - :after magit) +(defun my/ensure-machine-works-as-expected () + "Run all my personal tests to ensure Emacs behaves as I expect it to." + (interactive) + (load-file "init-test.el") + (ert t) + (ert-results-pop-to-timings)) #+end_src -1. Make a file =~/.authinfo= whose top-most line is =machine api.github.com login ⟪𝑼𝑺𝑬𝑹𝑵𝑨𝑴𝑬⟫^forge password ⟪𝑻𝑶𝑲𝑬𝑵⟫= where ⟪⋯⟫ - should be replaced by your Github username and a token ---which can be [[https://github.com/settings/tokens][created from this page]] with [[https://magit.vc/manual/forge/Token-Creation.html][permissions]] for - ~repo, user, read:org~ checked-off. - - For more detailed information, see [[https://magit.vc/manual/ghub/Creating-and-Storing-a-Token.html][this Forge manual page]]. +Anyhow, besides boring word completion, let's add support for [[https://github.com/dunn/company-emoji][emojis]]. +#+begin_src emacs-lisp +(use-package company-emoji + :config (add-to-list 'company-backends 'company-emoji)) +#+end_src -2. Now open a Magit status buffer for a repository and run ~M-x forge-pull~, after which you should be able to see all issues and pull requests. - - Alternatively, press ~N~ to see the forge menu. - - Press ~N f f~ to pull latest information from the repository. - + Note how pulling data from a forge’s API works the same way as pulling Git data does; you do it explicitly when - you want to see the work done by others. - + We can also “c”reate a new “p”ull request or a new “i”ssue with ~N c p~ or ~N c i~, respectively. - + [[https://www.youtube.com/watch?v=fFuf3hExF5w&ab_channel=ZaisteProgramming][~5 min video]] +For example: 🥞 💻 🐵 ✉️😉 🐬 🌵. -To *review a pull request* press tab on ~Pull Requests~ then press ~b y~ to checkout that branch locally. +➡️On a new line, write ~:~ then any letter to have a tool-tip appear. +All emoji names are lowercase. ◀ -** Pretty Magit Commit Leaders -:PROPERTIES: -:CUSTOM_ID: Pretty-Magit-Commit-Leaders -:END: -⟨ Following [[http://www.modernemacs.com/post/pretty-magit/][Pretty Magit - Integrating commit leaders | Modern Emacs]] ⨾⨾ Code -comes from there as well. Notable alteration: Helm compleition shows -description of leaders. ⟩ ++ On MacOS, ~C-⌘-SPC~ brings up an emoji picker, where one drags desired emojis to + textual areas. ++ [[https://gist.github.com/rxaviers/7360908][Here]] is a list of emoji ---all supported by Github. -Add faces to Magit to achieve icon and colored commit leaders. I also integrate -Helm to prompt a leader when committing so there's no need to remember or type -out completely every leader we choose. -- /It's not just aesthetics. It's about visual clarity./ -- [[https://www.reddit.com/r/emacs/comments/6jegis/comment/djeo89w/?utm_source=share&utm_medium=web2x&context=3][Here]] is an alternate approach: Add icons based on words mentioned in commit titles ---no leaders required. +The libraries ~emojify~ and ~emojify-logos~ provides cool items like =:haskell: +:emacs: :org: :ruby: :python:=. Unfortunately they do not easily export to html +with org-mode, so I'm not using them. +:Alternatives: +#+BEGIN_SRC emacs-lisp +(use-package emojify + :config (setq emojify-display-style 'image) + :init (global-emojify-mode 1)) ;; Will install missing images, if need be. +#+END_SRC -#+begin_details "Boring details ~ See linked article instead" -#+begin_src emacs-lisp -(cl-defmacro pretty-magit (WORD ICON PROPS &optional (description "") NO-PROMPT?) - "Replace sanitized WORD with ICON, PROPS and by default add to prompts." - `(prog1 - (add-to-list 'pretty-magit-alist - (list (rx bow (group ,WORD (eval (if ,NO-PROMPT? "" ":")))) - ,ICON ',PROPS)) - (unless ,NO-PROMPT? - (add-to-list 'pretty-magit-prompt (cons (concat ,WORD ": ") ,description))))) +(use-package emojify-logos +:after emojify) -(setq pretty-magit-alist nil) -(setq pretty-magit-prompt nil) +For example, +#+begin_src emacs-lisp :tangle no + :emacs: :haskell: :org: :c: :wink: :ruby: :python: :c: :html5: #+end_src -#+end_details +Yields :emacs: :haskell: :org: :c: :wink: :ruby: :python: :c: :html5:. Run ~(emojify-insert-emoji)~ to see more. -My personal choices for leaders are: -#+begin_src emacs-lisp -(pretty-magit "Add" ?➕ (:foreground "#375E97" :height 1.2) "✅ Create a capability e.g. feature, test, dependency.") -(pretty-magit "Delete" ?❌ (:foreground "#375E97" :height 1.2) "❌ Remove a capability e.g. feature, test, dependency.") -(pretty-magit "Fix" ?🔨 (:foreground "#FB6542" :height 1.2) "🐛 Fix an issue e.g. bug, typo, accident, misstatement.") -(pretty-magit "Clean" ?🧹 (:foreground "#FFBB00" :height 1.2) "✂ Refactor code; reformat say by altering whitespace; refactor performance.") -(pretty-magit "Document" ?📚 (:foreground "#3F681C" :height 1.2) "ℹ Refactor of documentation, e.g. help files.") -(pretty-magit "Feature" ?⛲ (:foreground "slate gray" :height 1.2) "⛳ 🇮🇶🇨🇦 A milestone commit - flagpost") -(pretty-magit "Generate" ?🔭 (:foreground "slate gray" :height 1.2) "Export PDF/HTML or tangle raw code from a literate program") ;; Generating artefacts -(pretty-magit "master" ? (:box t :height 1.2) "" t) -(pretty-magit "origin" ?🐙 (:box t :height 1.2) "" t) -;; Commit leader examples: https://news.ycombinator.com/item?id=13889155. -;; -;; Cut ~ Remove a capability e.g. feature, test, dependency. -;; Bump ~ Increase the version of something e.g. dependency. -;; Make ~ Change the build process, or tooling, or infra. -;; Start ~ Begin doing something; e.g. create a feature flag. -;; Stop ~ End doing something; e.g. remove a feature flag. -#+end_src +Rather than remember all of these, let's install [[https://github.com/syohex/emacs-ac-emoji][ac-emoji]] +for completion of emoji names. -#+begin_details "Boring details ~ See linked article instead" -#+begin_src emacs-lisp -(defun add-magit-faces () - "Add face properties and compose symbols for buffer from pretty-magit." - (interactive) - (with-silent-modifications - (--each pretty-magit-alist - (-let (((rgx icon props) it)) - (save-excursion - (goto-char (point-min)) - (while (search-forward-regexp rgx nil t) - (compose-region - (match-beginning 1) (match-end 1) icon) - (when props - (add-face-text-property - (match-beginning 1) (match-end 1) props)))))))) - -(advice-add 'magit-status :after 'add-magit-faces) -(advice-add 'magit-refresh-buffer :after 'add-magit-faces) - - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(use-package ac-emoji + :init (ac-emoji-setup) +) +:End: -(setq use-magit-commit-prompt-p nil) -(defun use-magit-commit-prompt (&rest args) - (setq use-magit-commit-prompt-p t)) +**** Documentation Pop-Ups +:PROPERTIES: +:CUSTOM_ID: Documentation-Pop-Ups +:END: -(defun magit-commit-prompt () - "Magit prompt and insert commit header with faces." - (interactive) - (when use-magit-commit-prompt-p - (setq use-magit-commit-prompt-p nil) - (thread-last (--map (format "%s %s" (car it) (cdr it)) pretty-magit-prompt) - (completing-read "Insert commit leader ∷ ") - ;; My “Generate:” commit type has one use case, for now; so let's insert it filled-in. - (funcall (lambda (it) (if (s-starts-with? "Generate:" it) it (car (s-split " " it))))) - (insert) - (end-of-line)) - (add-magit-faces))) +Let [[https://github.com/expez/company-quickhelp][documentation pop-up]] when we pause on a completion. ++ For example, open an ELisp buffer and enter =s-j= and see a list of completions, including =s-join=, + pause for a sec and a documentation tooltip appears. +This is very useful when editing in a particular coding language, say via +~C-c '~ for org-src blocks. Or when working in a language-specific buffer. +#+BEGIN_SRC emacs-lisp +(use-package company-quickhelp + :config + (setq company-quickhelp-delay 0.1) + (company-quickhelp-mode) + ;; Especially when learning a new language, looking up its definition/docstring can be helpful. + ;; Note: I use “M-!” everywhere else to mean “define word at point”. + ) +;; TODO: Consider adding a hook to company mode to immediately call (company-quickhelp--show), in prog-modes +#+END_SRC -(remove-hook 'git-commit-setup-hook 'with-editor-usage-message) -(add-hook 'git-commit-setup-hook 'magit-commit-prompt) -(advice-add 'magit-commit :after 'use-magit-commit-prompt) -#+end_src -#+end_details +This ~M-!~ is from plain ~company~, but I wanted to define it here beside the +quickhelp setup since they're related utilities. -** COMMENT Version Control with SVN ---Using Magit! :Disabled: +*** Intro to Snippets :PROPERTIES: -:CUSTOM_ID: Version-Control-with-SVN-Using-Magit +:CUSTOM_ID: Intro-to-Snippets :END: -| /Disabled: I seldom work with SVN anymore./ | +A *snippet, template, mechanism* is a tool that when you press some keystrokes +inserts some text, possibly with some fields (‘blanks’) to fill in. Possibly +interesting read: ++ [[https://cupfullofcode.com/blog/2013/02/26/snippet-expansion-with-yasnippet/index.html][Snippet Expansion With Yasnippet: Save Yourself Keystrokes and Headaches]] + ---a nice before introduction to Yasnippet (“Yet another snippet”) ++ [[https://jpace.wordpress.com/2012/10/20/tweaking-emacs-snippets/][Tweaking Emacs: Snippets]] ---a brief article on using snippets for uniformity + across languages and to mitigate verbosity of weak languages (i.e., those + without macros). -Let's use git as an interface to subversion repositories so that we can continue -to use =magit= as our version control interface. The utility to do so is called -=git svn= ---note =git 𝒳= on a MacOS is the same as =git-𝒳= on other systems. +-------------------------------------------------------------------------------- -#+begin_src emacs-lisp :tangle no -(use-package magit-svn - :hook (magit-mode . magit-svn-mode)) -#+end_src +[[http://joaotavora.github.io/yasnippet/snippet-development.html][Yasnippet]] is a pleasant utility for template expansion with the alluring +feature to allow arbitrary Lisp code to be executed during expansion. +The declaration of templates is verbose, requiring a particular file +hierarchy, as such I utilise [[https://github.com/Kungsgeten/yankpad][Yankpad]] which allows me to employ +an Org-mode approach: Each template corresponds to an org heading of +the form ~Key:Words:For:Expansion:Here: name of snippet here~ and the +template body is then the body of the org heading. +Any of ~Key, Words, For, Expansion, Here~ will rewrite into the body +of the org tree. This is much more terse, and I even don't bother +with that; instead preferring to tangle my templates using yankpad +as a mere interface. It is important to note that Yankpad also provides +features that are not in Yassnippet, such as allowing arbitrary language +code to be executed ---one simply uses an org-src block! -Here's an example. The following command checksout an SVN repo; afterwhich we may open a file -there and do =M-x magit-status= to get the expected porcelain git interface ^_^ -#+begin_src emacs-lisp :tangle no -(async-shell-command "mkdir ~/2fa3; git svn clone --username alhassm https://websvn.mcmaster.ca/csse2fa3/2019-2020_Term2 ~/2fa3/") -#+end_src +| [[https://www.philnewton.net/blog/exploring-emacs-yasnippet/][Here]] is a nice self-contained tutorial. | +| Also, [[https://www.reddit.com/r/emacs/comments/o282fq/yasnippet_snippetstemplating_introductiontutorial/][YASnippet snippets/templating Introduction/Tutorial — Straightforward Emacs: emacs]] | -In the magit buffer, we may now use the ~N~ key which wraps the =git svn= -subcommands =fetch, rebase, dcommit, branch, tag=. For example: +There can only be one major completion backend for any mode, but +other backends can serve as secondary ones. Here's a function to +make ~company-yankpad~ a secondary of all existing backends. +#+begin_src emacs-lisp +;; Add yasnippet support for all company backends +;; +(cl-defun my/company-backend-with-yankpad (backend) + "There can only be one main completition backend, so let's + enable yasnippet/yankpad as a secondary for all completion + backends. -1. Make changes to a file. -2. ‘Stage’ them with ~s~ and ‘commit’ them with ~c~. -3. ‘Push’ changes with ~N c~. + Src: https://emacs.stackexchange.com/a/10520/10352" -We get to pretend we're using ~git~ even though the underlying mechanism is ~svn~! -# Indeed, in a terminal ~git svn log~ will show an ~svn~ log with single pushed commits. + (if (and (listp backend) (member 'company-yankpad backend)) + backend + (append (if (consp backend) backend (list backend)) + '(:with company-yankpad)))) +#+end_src -For move on =git svn=, see [[https://gist.github.com/rickyah/7bc2de953ce42ba07116][A simple guide to git-svn]] or [[https://www.viget.com/articles/effectively-using-git-with-subversion/][Effectively using Git with - Subversion]]. +#+begin_src emacs-lisp +;; Yet another snippet extension program +(use-package yasnippet + :config + (yas-global-mode 1) ;; Always have this on for when using yasnippet syntax within yankpad + ;; respect the spacing in my snippet declarations + (setq yas-indent-line 'fixed)) -(If I need to work with svn repos often enough, I'd extend my ~maybe-clone~ - utility above to account for them.) +;; Alternative, Org-based extension program +(use-package yankpad -** Highlighting TODO-s & Showing them in Magit -:PROPERTIES: -:CUSTOM_ID: Highlighting-TODO-s-Showing-them-in-Magit -:END: + :config + ;; Location of templates + (setq yankpad-file "~/.emacs.d/yankpad.org") -Sometimes it's nice to flag a chunk of text by its author, such as ‘ [[color:#dc8cc3][MA]] ’ for -‘M’usa ‘A’l-hassy, or ‘ [[color:#d0bf8f][HACK]] ’ for text that needs to be improved. Such flags -stand out from other text by being coloured and bold. + ;; Ignore major mode, always use defaults. + ;; Yankpad will freeze if no org heading has the name of the given category. + (setq yankpad-category "Default") -#+BEGIN_SRC emacs-lisp -;; NOTE that the highlighting works even in comments. -(use-package hl-todo - ;; I want todo-words highlighted in prose, not just in code fragements. - :hook (org-mode . hl-todo-mode) - :config - ;; Adding new keywords - (cl-loop for kw in '("TEST" "MA" "WK" "JC") - do (add-to-list 'hl-todo-keyword-faces (cons kw "#dc8cc3"))) - ;; Enable it everywhere. - (global-hl-todo-mode)) -#+END_SRC + ;; Load the snippet templates ---useful after yankpad is altered + (yankpad-reload) -We've added few to the default flag keywords so that in total we have the -following flags ---where any sequence of at least 3 [[color:#cc9393][XXX]] are considered flags. -#+BEGIN_SRC emacs-lisp :results replace raw value :exports results :eval no-export :tangle no -(cl-loop for (k . f) in hl-todo-keyword-faces - collect (format "[[color:%s][%s]]" f k)) -#+END_SRC + ;; Set company-backend as a secondary completion backend to all existing backends. + (setq company-backends (mapcar #'my/company-backend-with-yankpad company-backends))) +#+end_src -#+RESULTS: -([[color:#dc8cc3][JC]] [[color:#dc8cc3][WK]] [[color:#dc8cc3][MA]] [[color:#dc8cc3][TEST]] [[color:#d0bf8f][HOLD]] [[color:#cc9393][TODO]] [[color:#dca3a3][NEXT]] [[color:#dc8cc3][THEM]] [[color:#7cb8bb][PROG]] [[color:#7cb8bb][OKAY]] [[color:#5f7f5f][DONT]] [[color:#8c5353][FAIL]] [[color:#afd8af][DONE]] [[color:#d0bf8f][NOTE]] [[color:#d0bf8f][KLUDGE]] [[color:#d0bf8f][HACK]] [[color:#d0bf8f][TEMP]] [[color:#cc9393][FIXME]] [[color:#cc9393][XXX+]]) +With these settings, along with the ~company~ backend, I may type a keyword then +kbd:TAB it into expansion. -:Alternate_approach_using_font_lock: -#+begin_src emacs-lisp -(defun add-watchwords () "Add TODO: words to font-lock keywords." - (font-lock-add-keywords nil - '(("\\(\\ jump to first and final semantic units. -;; If pressed twice, they go to physical first and last positions. -(use-package beginend - :config (beginend-global-mode)) -#+END_SRC -** COMMENT Get CheatSheets and view them easily -:PROPERTIES: -:CUSTOM_ID: Get-CheatSheets-and-view-them-easily -:END: +In this case, ~yas-text~ is equivalent to (~yas-field-value 1)~; +it generally refers to the value of the field being mirrored with ~${n: ⋯yas-text⋯}~. -#+begin_src emacs-lisp -(defvar my/cheatsheet/cached-topics nil) -(cl-defun my/cheatsheet (&optional topic) - "Clone Al-hassy's ⟨TOPIC⟩CheatSheet repository when called from Lisp; visit the pretty HTML page when called interactively. +However, going through pop-ups takes precious time ---besides being slightly annyoing. +Let's introduce a template for my most utilised kind of language blocks. +#+begin_example +,** s_org: src block for org +,#+begin_src org +$0 +,#+end_src +#+end_example -- Example usage: (my/cheatsheet \"Vue\") -- Example usage: M-x my/cheatsheet RET Vue RET." - (interactive) - (if (not topic) - (browse-url (format "https://alhassy.github.io/%sCheatSheet" (completing-read "Topic: " my/cheatsheet/cached-topics))) - (push topic my/cheatsheet/cached-topics) - (maybe-clone (format "https://github.com/alhassy/%sCheatSheet" topic)))) +However, doing this for each language I want is a waste of time and textual +space. Why? *The purpose of templates is to reduce repetition,* yet the above +block would be repeated with only 3 parts ‘unknown’: The expansion keyword, the +description, and the org-mode source block name. Whence, the template /text/ is +generated by the following basic loop ---whose source block is named +~my-org-lang-templates~. +#+name: my-org-lang-templates +#+begin_src emacs-lisp :tangle no :wrap "src org :tangle ~/.emacs.d/yankpad.org" :exports code :results replace drawer +;; We make an org BLOCK snippet template for each LANG the user has declared. +;; +(cl-loop for (shortcut block takes-language-argument? default-text) + in '(("s_" "src" t) + ("is_" "inline source" t) ;; Treated specially below + ("e_" "example" t) + ("q_" "quote") + ("v_" "verse") + ("c_" "center") + ("ex_" "export") ;; only HTML and LATEX + ;; https://alhassy.github.io/org-special-block-extras/#Summary + ("p_" "parallel" nil "\n$0\n#+columnbreak:\n") + ("d_" "details" nil "${1:title}\n$0") + ("ed_" "edcomm" nil "${1:editor}\n$0") + ("doc_" "documentation" nil "${1: mandatory entry name}\n$0") + ("def_" "latex-definitions")) + for languages = (if takes-language-argument? + (-cons* "org" "agda2" "any" ;; Extra ‘languages’ + ;; Also include whatever languages we've loaded for literate programming. + (--map (symbol-name (car it)) org-babel-load-languages)) + '("")) ;; The “empty language” + concat (cl-loop for lang in languages + for key = (concat shortcut + (if (s-blank? lang) block lang)) + for description = (if (s-blank? lang) + block + (concat + block " for " lang)) + concat (if (equal "is_" shortcut) + (concat "\n** " key ": " description + "\nsrc_" lang "[:exports code]{$1} $0") + (concat "\n** " key ": " description + "\n#+begin_" block " " lang + (or default-text "\n$0") + "\n#+end_" block "\n")))) #+end_src -Let's actually get some repos locally, and use: ~M-x my/cheatsheet~ to view the pretty HTML (or PDF) sheets. -#+begin_src emacs-lisp -(mapcar #'my/cheatsheet '("ELisp" "GojuRyu" "Rust")) ; Python Prolog Vue Agda JavaScript - ; Clojure Ruby Oz Coq Cats Haskell FSharp OCaml +The /resulting text/ of this block, generated below, is tangled to our yankpad by +utilising a [[https://www.gnu.org/software/emacs/manual/html_node/org/Noweb-reference-syntax.html][noweb]] source block invocation. An example of the resulting text is +the above ~s_org~ block. The result is (last I checked) *83* template expansions +---that would have been a bit much to write by hand. +#+begin_example org +,#+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes +<> +,#+end_src +#+end_example +# ActuallyDoIt +#+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes :comments none +<> #+end_src -* Literate Programming -:PROPERTIES: -:CUSTOM_ID: Literate-Programming -:END: - -Org-mode lets us run chunks of code anywhere, then feed their outputs to other -chunks of code in /possibly different/ programming languages: Org is a -meta-(programming language). +# The “:eval never-export” means that this block is never tangled on document +# export, C-c C-e. -Importantly, this means we can write text and whenever we need the result of -some computation, we can place it there and then and only request its result -appear in PDF/HTML export. The result is a single document. +#+begin_box +Now ~s_~, due to company mode, brings up a list of languages that I can then +scroll down through, then “enter” upon to expand. Moreover, the prefix ~s_~ means +that the key is mostly irrelevant, since I needn't remember it because +company-mode immediately lists possible completions /along/ with the /descriptions/ +for the snippets. Likewise for examples with ~e_~ or quotes with ~q_~. Super neat +stuff :-) -( There is the [[https://github.com/minad/org-modern][org-modern]] package, which provides a modern look-and-feel: It -makes Org look less like a markup and more like a word editor. Nice stuff. ) + Ain't this reminiscent of meta-programming ;-) +#+end_box +Using =noweb= invocations, any time the tangling is performed, the yankpad +is kept up to date ---no personal intervention from myself. -** High Speed Literate Programming -:PROPERTIES: -:CUSTOM_ID: High-Speed-Literate-Programming -:END: +# Neat, but not what I want. +# https://github.com/abo-abo/auto-yasnippet -*** Manipulating Sections -:PROPERTIES: -:CUSTOM_ID: Manipulating-Sections -:END: +:Fun_albeit_useless_exercise: -#+BEGIN_SRC emacs-lisp -(setq org-use-speed-commands t) -#+END_SRC +Let's push this frontier a bit more … -This enables the [[http://notesyoujustmightwanttosave.blogspot.com/2011/12/org-speed-keys.html][Org Speed Keys]] so that when the cursor is at the beginning of a -headline, we can perform fast manipulation & navigation using the standard Emacs -movement controls, such as: -+ [[kbd:#]] toggle ~COMMENT~-ing for an org-header. -+ [[kbd:s]] toggles [[doc:narrow-to-region][“narrowing”]] to a subtree; i.e., hide the rest of the document. +In expressive languages like Agda, one can not only be type polymorphic but also +‘level polymorphic’ ---since types constitute a hierarchy where a ‘type’ is +uninterestingly an ‘element’ of a higher ‘type’, ad infinitum. For example, the +type of a level polymorphic ′choice’ function would be ~{a : Level} (A : Set a) → A~ +---note that such a choice function cannot exist since for any type ~A~ it returns +an element of ~A~, then what of the empty type. Anyhow, the template ~{${1:a} : +Level} → (${2:A} : Set $1) → $0~ would suffice to make this happen. Yet, what if +we wanted /n/-many sets? - If you narrow to a subtree then any export, kbd:C-c_C-e, will joyously only - consider the narrowed detail. -+ [[kbd:u]] for going to upwards to parent heading - * [[kbd:i]] insert a new same-level heading below current heading. -+ [[kbd:c]] for cycling structure below current heading, or ~C~ for cycling global structure. -+ [[kbd:w]] refile current heading; options list pops-up to select which heading to move - it to. Neato! - - [[kbd:g]] to go to another heading, without refiling anything. - #+begin_src emacs-lisp -;; When refiling, only show me top level headings [Default]. Sometimes 2 is useful. -;; When I'm refiling my TODOS, then give me all the freedom. -(setq org-refile-targets '((nil :maxlevel . 1) - (org-agenda-files :maxlevel . 9))) +Make a function that takes /n ≤ 26/ as input, produces a list of +levels, then uses each level to produce a list of type names ;-) +:End: -;; Maybe I want to refile into a new heading; confirm with me. -(setq org-refile-allow-creating-parent-nodes 'confirm) +With the advent of org-special-block-extras, I've made increased usage of links +--such as ~green:hello~ which yields green:hello and ~[[kbd:][green]]~ which yields +[[kbd:][green]]. +#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none +,** ll_make_a_link: insert a link template +${1:`(let* ((τ (read-string "Link type: ")) + (δ (read-string "Link Description: ")) + (⊤ (if (s-contains? ":" τ) τ (s-concat τ ":")))) + (format "[[%s][%s]]" ⊤ δ))`} $0 +#+END_SRC -;; Use full outline paths for refile targets -;; When refiling, using Helm, show me the hierarchy paths -(setq org-outline-path-complete-in-steps nil) -(setq org-refile-use-outline-path 'file-path) - #+end_src -+ kbd:n/p for next/previous /visible/ heading. -+ kbd:f/b for jumping forward/backward to the next/previous /same-level/ heading. -+ kbd:D/U move a heading down/up. -+ kbd:L/R recursively promote (move leftwards) or demote (more rightwards) a heading. -+ kbd:I/O clock In/Out to the task defined by the current heading. - - Keep track of your work times! - - [[kbd:v]] view agenda. -+ kbd:t/,/:/e to add a TODO state, priority level, tag, or effort estimate - * kbd:1/2/3 to mark a heading with priority, highest to lowest. -+ [[kbd:^]] sort children of current subtree; brings up a list of sorting options. -+ kbd:k/@/a to kill or mark or archive the current subtree -+ [[kbd:o]] to open a link mentioned in the subtree then go to the link; a pop-up of - links appears. +*** Operating System Keyboard Symbols +:PROPERTIES: +:CUSTOM_ID: Operating-System-Keyboard-Symbols +:END: -We can add our own speed keys by altering the doc:org-speed-commands association -list variable; e.g., -#+begin_src emacs-lisp -;; TODO FIXME Crashes upon startup. -(when nil (add-to-list 'org-speed-commands (cons "P" #'org-set-property))) -;; Use ‘:’ and ‘e’ to set tags and effort, respectively. +Write ~os_~ then see a bunch of completions ;-) + +#+name: my-snippets-os-symbols +#+begin_src emacs-lisp :tangle no :wrap "src org :tangle ~/.emacs.d/yankpad.org" :exports code :results replace drawer +(cl-loop for (name expansion) + in '((os-command ⌘) + (os_option ⌥) + (os_alt ⌥) + (os_control ⌃) + (os_shift ⇧) + (os_backspace ⌫) + (os_delete ⌫) + (os_delete_forward ⌦) + (os_enter ⏎) + (os_return ⏎) + (os_escape ⎋) + (os_tab_right ⇥) + (os_tab_left ⇤) + (os_caps_lock ⇪) + (os_eject ⏏)) + concat + (format "** %s: %s Operating System Keyboard Symbol\n%s\n" name expansion expansion)) #+end_src -# To see the commands available, execute ~M-x org-speed-command-help~. -| ⇒ Moreover, [[kbd:?]] to see a complete list of keys available. ⇐ | +#+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes :comments none +<> +#+end_src -*** Seamless Navigation Between Source Blocks +*** Work Templates :PROPERTIES: -:CUSTOM_ID: Seamless-Navigation-Between-Source-Blocks +:CUSTOM_ID: Work-Templates :END: -The “super key” ---aka the command or windows key--- can be used to jump to the -previous, next, or toggle editing org-mode source blocks. -#+begin_src emacs-lisp -;; Overriding keys for printing buffer, duplicating gui frame, and isearch-yank-kill. -;; -(require 'org) -(use-package emacs - :bind (:map org-mode-map - ("s-p" . org-babel-previous-src-block) - ("s-n" . org-babel-next-src-block) - ("s-e" . org-edit-special) - :map org-src-mode-map - ("s-e" . org-edit-src-exit))) -#+end_src +#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none +,** ll_console_log: Log some JS variables -# Interestingly, ~s-l~ is “goto line”. +console.log("%c ******* LOOK HERE *******", "color: green; font-weight: bold;"); +console.log({ ${1:List the variables here whose values you want to log} }); +$0 -Note that we could have bound kbd:⌘+e to doc:org-edit-src-code / -doc:org-edit-src-exit, but instead chose the more general doc:org-edit-special -since, well, look at the tooltip documentation: This allows us to use kbd:⌘+e to -‘e’dit all kinds of Org entities ---including footnotes and export blocks. ( -Footnotes can be quickly produced with doc:org-footnote-new. ) +,** uuidgen: Insert the result of “uuidgen” and copy it to the clipboard -*** Modifying [[kbd:⟨return⟩]] +${1:`(-let [it (shell-command-to-string "uuidgen | tr '[:upper:]' '[:lower:]' | +pbcopy; pbpaste")] (message "Copied to clipboard, uuid: %s" it) it)`} +#+end_src + +*** Elisp Templates :PROPERTIES: -:CUSTOM_ID: Modifying-return +:CUSTOM_ID: Elisp-Templates :END: -+ [[kbd:C-⟨return⟩]] , [[kbd:C-S-⟨return⟩]] make a new heading where the latter marks it as a ~TODO~. -+ By default [[kbd:M-⟨return⟩]] makes it easy to work with existing list items, headings, - tables, etc by creating a new item, heading, etc. - -Usually we want a newline then we indent, let's make that the default. - #+BEGIN_SRC emacs-lisp -(add-hook 'org-mode-hook '(lambda () - (local-set-key (kbd "") 'org-return-indent)) - (local-set-key (kbd "C-M-") 'electric-indent-just-newline)) -#+END_SRC +The following snippets were rather useful as I began learning Lisp to construct +my editor of choice ---I love Emacs so much. Admittedly, I still need the first +one below and usually beat around the bush by using ~(cl-loop for ⋯ do ⋯)~, (doc:cl-loop), which is +‘noisier’ but easier to remember and to read for non-Lispers. +#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none +,** loop: Elisp's for each loop +(dolist (${1:var} ${2:list-form}) + ${3:body}) - Notice that I've also added another kind of return, for when I want to - break-out of the indentation approach and start working at the beginning of - the line. +,** defun: Lisp functions +(cl-defun ${1:fun-name} (${2:arguments}) + "${3:documentation}" + $0) -In summary: -| key | method | behaviour | -|------------------+---------------------------------------------+-----------------------------------| -| [[kbd:⟨return⟩]] | doc:org-return-indent | Newline with indentation | -| [[kbd:M-⟨return⟩]] | doc:org-meta-return | Newline with new org item | -| [[kbd:C-M-⟨return⟩]] | doc:electric-indent-just-newline | Newline, cursor at start | -| [[kbd:C-⟨return⟩]] | doc:org-insert-heading-respect-content | New heading /after/ current content | -| [[kbd:C-S-⟨return⟩]] | doc:org-insert-todo-heading-respect-content | Ditto, but with a ~TODO~ marker | +,** cond: Elisp conditionals +(cond (${1:scenario₁} ${2:response₁}) + (${3:scenario₂} ${4:response₂})) +#+end_src -*** COMMENT   ~C-a,e,k~ and Yanking of sections :Does_not_work: +*** Equational Templates :PROPERTIES: -:CUSTOM_ID: C-a-e-k-and-Yanking-of-sections +:CUSTOM_ID: Equational-Templates :END: -On an org-heading, kbd:C-a goes to /after/ the star, heading markers. To use -speed keys, run kbd:C-a_C-a to get to the star markers. +To show ~ℒ = ℛ~, one starts at the complicated side, say /ℒ/, then, with the aim of +simplification, tries to end at the simpler side, /𝓡/. Along the way, one +justifies each step of the calculation. This approach is popular in the proof +assistant Agda; [[https://alhassy.github.io/PathCat/][Examples]]. Read more about [[http://www.mathmeth.com/][informal calculational proofs]]. -kbd:C-e goes to the end of the heading, not including the tags. -#+begin_src emacs-lisp -(setq org-special-ctrl-a/e t) -#+end_src +#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none +,** fun: Function declaration with type signature -kbd:C-k no longer removes tags, if activated in the middle of a heading's name. -#+begin_src emacs-lisp -(setq org-special-ctrl-k t) ;; MA: Does not work …! -#+end_src +${1:fun-name} : ${2:arguments} +$1 ${3:args} = ?$0 -When you yank a subtree and paste it alongside a subtree of depth /‘d’/, then the -yanked tree's depth is adjusted to become depth /‘d’/ as well. If you don't want -this, then refile instead of copy-pasting. -#+begin_src emacs-lisp -(setq org-yank-adjusted-subtrees t) +,** eqn_begin: Start a ≡-Reasoning block in Agda + +begin + ${1:complicated-side} +$0≡⟨ ${3:reason-for-the-equality} ⟩ + ${2:simpler-side} +∎ + +,** eqn_step: Insert a step in a ≡-Reasoning block in Agda +≡⟨ ${2:reason-for-the-equality} ⟩ + ${1:new-expression} +$0 #+end_src -** Executing code from ~src~ blocks + +One expands ~eqn_begin~, tabs to fill in the three main locations, then +/immediately/ types ~eqn_step~ to produce a new step in a calculational proof. + +*** Fixed replies :PROPERTIES: -:CUSTOM_ID: Executing-code-from-src-blocks +:CUSTOM_ID: Fixed-replies :END: -For example, to execute a shell command in Emacs, write a ~src~ with a shell -command, then ~C-c c-c~ to see the results. Emacs will generally query you to -ensure you're confident about executing the (possibly dangerous) code block; -let's stop that: -#+BEGIN_SRC emacs-lisp :tangle init.el -;; Seamless use of babel: No confirmation upon execution. -;; Downside: Could accidentally evaluate harmful code. -(setq org-confirm-babel-evaluate nil) +Here are some replies that I sometimes need to produce; e.g., to people who +insist their way is the right way. -;; Never evaluate code blocks upon export and replace results when evaluation does occur. -;; For a particular language 𝑳, alter ‘org-babel-default-header-args:𝑳’. -(setq org-babel-default-header-args - '((:results . "replace") - (:session . "none") - (:exports . "both") - (:cache . "no") - (:noweb . "no") - (:hlines . "no") - (:tangle . "no") - (:eval . "never-export"))) -#+END_SRC +#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none +,** reply_opinionated_pantomath: What to say to, e.g., an arrogant academic +Your certainty inspires me to continuing exploring, and I may arrive at your +point of view, but I'm going to need more evidence first. -# A worked out example can be obtained as follows: ~< or xD emoticons.") - ;; Please use an awesome shell. - (shell-pop-term-shell "/bin/zsh")) -#+END_SRC -# There are also a number of hooks for shell-pop. E.g., to unconditionally kill the buffer afterwards. + (😅 "Grinning Face with Sweat" + "Intended to depict nerves or discomfort but + commonly used to express a close call, as if + saying ‘Whew!’ and wiping sweat from the + forehead. ") -#+begin_details Oh My Zsh -Now that we have access to quick pop-up for a shell, let's get a pretty and -practical shell: [[https://www.howtogeek.com/362409/what-is-zsh-and-why-should-you-use-it-instead-of-bash/][zsh]] along with the [[https://ohmyz.sh/][Oh My Zsh]] community configurations give us: + (🤣 "Rolling on the Floor Laughing" + "Often conveys hysterical laughter more intense + than 😂 Face With Tears of Joy.") -1. ~brew install zsh~ -2. ~sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"~ + (😂 "Face with Tears of Joy") + (🙂 "Slightly Smiling Face") + (🙃 "Upside-Down Face") + (😉 "Winking Face") + (😊 "Smiling Face with Smiling Eyes") + (😇 "Smiling Face with Halo") + (🥰 "Smiling Face with Hearts") + (😍 "Smiling Face with Heart-Eyes") + (🤩 "Star-Struck") + (😘 "Face Blowing a Kiss") + (😗 "Kissing Face") + (☺️ "Smiling Face") + (😚 "Kissing Face with Closed Eyes") + (😙 "Kissing Face with Smiling Eyes") + (🥲 "Smiling Face with Tear") + (😋 "Face Savoring Food") + (😛 "Face with Tongue") + (😜 "Winking Face with Tongue") + (🤪 "Zany Face") + (😝 "Squinting Face with Tongue") + (🤑 "Money-Mouth Face") + (🤗 "Hugging Face") + (🤭 "Face with Hand Over Mouth") + (🤫 "Shushing Face") + (🤔 "Thinking Face") + (🤐 "Zipper-Mouth Face") + (🤨 "Face with Raised Eyebrow") + (😐 "Neutral Face") + (😑 "Expressionless Face") + (😶 "Face Without Mouth") + (😏 "Smirking Face") + (😒 "Unamused Face") + (🙄 "Face with Rolling Eyes") + (😬 "Grimacing Face") + (🤥 "Lying Face") + (😌 "Relieved Face") + (😔 "Pensive Face") + (😪 "Sleepy Face") + (🤤 "Drooling Face") + ("Sleeping Face") + (😷 "Face with Medical Mask") + (🤒 "Face with Thermometer") + (🤕 "Face with Head-Bandage") + (🤢 "Nauseated Face") + (🤮 "Face Vomiting") + (🤧 "Sneezing Face") + (🥵 "Hot Face") + (🥶 "Cold Face") + (🥴 "Woozy Face") + (😵 "Dizzy Face") + (🤯 "Exploding Head") + (🤠 "Cowboy Hat Face") + (🥳 "Partying Face") + (🥸 "Disguised Face") + (😎 "Smiling Face with Sunglasses") + (🤓 "Nerd Face") + (🧐 "Face with Monocle") + (😕 "Confused Face") + (😟 "Worried Face") + (🙁 "Slightly Frowning Face") + (☹️ "Frowning Face") + (😮 "Face with Open Mouth") + (😯 "Hushed Face") + (😲 "Astonished Face") + (😳 "Flushed Face") + (🥺 "Pleading Face") + (😦 "Frowning Face with Open Mouth") + (😧 "Anguished Face") + (😨 "Fearful Face") + (😰 "Anxious Face with Sweat") + (😥 "Sad but Relieved Face") + (😢 "Crying Face") + (😭 "Loudly Crying Face") + (😱 "Face Screaming in Fear") + (😖 "Confounded Face") + (😣 "Persevering Face") + (😞 "Disappointed Face") + (😓 "Downcast Face with Sweat") + (😩 "Weary Face") + (😫 "Tired Face") + (🥱 "Yawning Face") + (😤 "Face with Steam From Nose") + (😡 "Pouting Face") + (😠 "Angry Face") + (🤬 "Face with Symbols on Mouth") + ) + for nom = (s-replace " " "_" name) + for desc = (s-collapse-whitespace (or description "")) + concat (concat + ;; f_… ⇒ get emoji from company menu showing only name & emoji + (format "\n** f_%s: %s %s \n%s" nom emoji "" emoji) + ;; fd_… ⇒ get emoji from company menu showing name, emoji, & ‘d’escription + (format "\n** fd_%s: %s %s \n%s" nom emoji desc emoji))) +#+end_src - This installs everything ^_^ +#+begin_src emacs-lisp +;; Get all unicode emojis to appear within Emacs +;; See also: https://emacs.stackexchange.com/questions/5689/force-a-single-font-for-all-unicode-glyphs?rq=1 +(unless noninteractive (set-fontset-font t nil "Apple Color Emoji")) +#+end_src +## +## #+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes :exports none +## <> +## #+end_src +## +***   =my_⋯= Templates to obtain User Information +:PROPERTIES: +:CUSTOM_ID: my-Templates-to-obtain-User-Information +:END: -#+BEGIN_SRC emacs-lisp -;; Be default, Emacs please use zsh -;; E.g., M-x shell -(unless noninteractive (setq shell-file-name "/bin/zsh")) -#+END_SRC +Let's add templates for links to common user information ^_^ -Out of the box, zsh comes with -+ git support; the left side indicates which branch we're on and - whether the repo is dirty, ✗. -+ Recursive path expansion; e.g., ~/u/lo/b TAB~ expands to ~/usr/local/bin/~ -+ Over [[https://github.com/ohmyzsh/ohmyzsh/wiki/Plugins#apache2-macports][250+ Plugins]] and [[https://github.com/ohmyzsh/ohmyzsh/wiki/Themes][125+ Themes]] that are enabled by simply - mentioning their name in the ~.zshrc~ file. +#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none +,** my_name: User's name +`user-full-name` -The defaults have been good enough for me, for now ---as all else is achieved -via Emacs ;-) -#+end_details +,** my_email: User's email address +`user-mail-address` -Also, there's the [[https://tldr.sh/][tldr]] tool which aims to be like terse manuals for -commandline-tools in the style of practical example uses cases: ~tldr 𝒳~ yields a -number of ways you'd actually use 𝒳. ( In Emacs, [[kbd:C-t tldr 𝒳 ⟨return⟩]]. ) -#+BEGIN_SRC emacs-lisp -(system-packages-ensure "tldr") -#+END_SRC +,** my_github: User's Github repoistory link +https://github.com/alhassy/ -** TODO COMMENT Snippets ---Template Expansion -:PROPERTIES: -:CUSTOM_ID: Snippets-Template-Expansion -:END: +,** my_emacsdrepo: User's version controlled Emacs init file +https://github.com/alhassy/emacs.d -It is common that there is a sequence of text that we tend to repeat -often, possibly with a name or some other parameter altered. -Such a ‘snippet’ could be written once then provided by a simple -Lisp insert command with the parameters being queried. Luckily, others -have written such pleasant utilities. +,** my_blog: User's blog website +https://alhassy.github.io/ -Besides snippets, there are words that we may want to repeat often but it can be -tedious to write them out in full. As such, we employ *[[green: word completion]]*; -which we also use to expand our snippets. -# For instance, I knew someone who writes ‘U’ all over the place -# since the word “universe” is too long to write -# and Emacs doesn't come with word completion. +,** my_webpage: User's organisation website +http://www.cas.mcmaster.ca/~alhassm/ + +,** my_twitter: User's Twitter profile +https://twitter.com/musa314 + +,** my_masters_thesis +A Mechanisation of Internal Galois Connections In Order Theory Formalised Without Meets +https://macsphere.mcmaster.ca/bitstream/11375/17276/2/thesis.pdf +#+end_src + +It may be useful to also have Org-link variants of these … -*** Word Completion +*** Templates from other places in my init :PROPERTIES: -:CUSTOM_ID: Word-Completion +:CUSTOM_ID: Activate-templates-from-other-places-in-my-init :END: -Let's enable [[https://company-mode.github.io/][“complete anything” mode]] ---it ought to start in half a second and -only need two characters to get going, which means word suggestions are provided -and so I need only type partial words then tab to get the full word! - -#+begin_src emacs-lisp -(use-package company - - :config - (global-company-mode 1) - (setq ;; Only 2 letters required for completion to activate. - company-minimum-prefix-length 2 - - ;; Search other buffers for compleition candidates - company-dabbrev-other-buffers t - company-dabbrev-code-other-buffers t - - ;; Show candidates according to importance, then case, then in-buffer frequency - company-transformers '(company-sort-by-backend-importance - company-sort-prefer-same-case-prefix - company-sort-by-occurrence) - - ;; Flushright any annotations for a compleition; - ;; e.g., the description of what a snippet template word expands into. - company-tooltip-align-annotations t +In this setup, I have some templates appear elsewhere, tagged with =:noweb-ref +templates-from-other-places-in-my-init=. They are presented in natural +positions, but can only occur to the machine after template expansion is setup. +Using org-mode, we are able to /present/ code in any order and /tangle/ it to +the order the compilers need it to be! - ;; Allow (lengthy) numbers to be eligible for completion. - company-complete-number t +Let's activate all such templates, now after template expansion has been setup. +#+begin_src org :tangle no +,#+begin_src org :noweb yes :tangle "~/.emacs.d/yankpad.org" :comments none +<> +,#+end_src +#+end_src - ;; M-⟪num⟫ to select an option according to its number. - company-show-numbers t +You can press kbd:C-c_C-v_C-v, doc:org-babel-expand-src-block, to see what this +block expands into... +#+begin_details Expansion +# This is also live. +#+begin_src org :noweb yes :tangle "~/.emacs.d/yankpad.org" :comments none +<> +#+end_src +#+end_details - ;; Show 10 items in a tooltip; scrollbar otherwise or C-s ^_^ - company-tooltip-limit 10 +Note: Since I've insisted that Org blocks are space sensative, any whitespace +before the ~<<⋯>>~ will propogate to the resulting extracted code. - ;; Edge of the completion list cycles around. - company-selection-wrap-around t +#+begin_box Warning! :background-color red +This section had +#+begin_src org :tangle no +:PROPERTIES: +:CUSTOM_ID: Templates-from-other-places-in-my-init +:END: +#+end_src +Which, as of Org 9.4, led to the entire section being tangled: This is what the +above incantation requested, but I thought it only worked on src blocks, having +the specified ~:noweb-ref~, not on ~:CUSTOM_ID:~ incidentally having the same +name. +#+end_box - ;; Do not downcase completions by default. - company-dabbrev-downcase nil +** Prettify inline source code +:PROPERTIES: +:CUSTOM_ID: Prettify-inline-source-code +:END: - ;; Even if I write something with the ‘wrong’ case, - ;; provide the ‘correct’ casing. - company-dabbrev-ignore-case nil +#+begin_src emacs-lisp +;; Show “ src_emacs-lisp[:exports results]{ 𝒳 } ” as “ ℰ𝓁𝒾𝓈𝓅﴾ 𝒳 ﴿ ”. +;; +(font-lock-add-keywords 'org-mode + '(("\\(src_emacs-lisp\\[.*]{\\)\\([^}]*\\)\\(}\\)" + (1 '(face (:inherit (bold) :foreground "gray65") display "ℰ𝓁𝒾𝓈𝓅﴾")) + (2 '(face (:foreground "blue"))) + (3 '(face (:inherit (bold) :foreground "gray65") display "﴿")) + ))) +;; +;; Let's do this for all my languages: +;; Show “ src_LANGUAGE[…]{ ⋯ } ” as “ ﴾ ⋯ ﴿ ”. +(cl-loop for lang in my/programming-languages + do (font-lock-add-keywords 'org-mode + `(( ,(format "\\(src_%s\\[.*]{\\)\\([^}]*\\)\\(}\\)" lang) + (1 '(face (:inherit (bold) :foreground "gray65") display "﴾")) + (2 '(face (:foreground "blue"))) + (3 '(face (:inherit (bold) :foreground "gray65") display "﴿")) + )))) - ;; Immediately activate completion. - company-idle-delay 0) +;; +(defun my/toggle-line-fontification () + "Toggle the fontification of the current line" + (interactive) + (defvar my/toggle-fontify/current-line -1) + (defvar my/toggle-fontify/on? nil) + (add-to-list 'font-lock-extra-managed-props 'display) + (let ((start (line-beginning-position)) (end (line-end-position))) + (cond + ;; Are we toggling the current line? + ((= (line-number-at-pos) my/toggle-fontify/current-line) + (if my/toggle-fontify/on? + (font-lock-fontify-region start end) + (font-lock-unfontify-region start end)) + (setq my/toggle-fontify/on? (not my/toggle-fontify/on?))) + ;; Nope, we've moved on to another line. + (:otherwise + (setq my/toggle-fontify/current-line (line-number-at-pos) + my/toggle-fontify/on? :yes_please_fontify) + (font-lock-unfontify-region start end))))) - ;; Use C-/ to manually start company mode at point. C-/ is used by undo-tree. - ;; Override all minor modes that use C-/; bind-key* is discussed below. - (bind-key* "C-/" #'company-manual-begin) + ;; TODO FIXME; maybe ignore: Wasted too much time here already. +;; (add-hook 'post-command-hook #'my/toggle-line-fontification nil t) +;; (font-lock-add-keywords nil '((my/toggle-line-fontification)) t) +#+end_src - ;; Bindings when the company list is active. - :bind (:map company-active-map - ("C-d" . company-show-doc-buffer) ;; In new temp buffer - ("" . company-complete-selection) - ;; Use C-n,p for navigation in addition to M-n,p - ("C-n" . (lambda () (interactive) (company-complete-common-or-cycle 1))) - ("C-p" . (lambda () (interactive) (company-complete-common-or-cycle -1))))) +** Unfold Org Headings when I perform a search -;; It's so fast that we don't need a key-binding to start it! +#+begin_src emacs-lisp +(setq org-fold-core-style 'overlays) #+end_src -Note that ~M-/~ goes through a sequence of completions ---and ~C-/~ manually begins -company mode at point. Besides the arrow keys, we can also use ~M-~ with ~n, p~ to -navigate the options /or/ use ~C-s~ to search the list of suggestions. -+ Company backends are available as separate packages. -+ Note that [[https://github.com/company-mode/company-mode/issues/360][by default]] company mode does not support completion for phrases - containing hyphens ---this can be altered, if desired. +* Staying Sane +:PROPERTIES: +:CUSTOM_ID: Staying-Safe +:END: -Let's add a test for some basic features of completition: -#+begin_src emacs-lisp :tangle init-test.el -(ert-deftest company-works-as-expected-in-text-mode () - :tags '(company) - (switch-to-buffer "*TESTING COMPANY MODE ~ Text*") +# See [[https://jeremykun.com/2020/01/14/the-communicative-value-of-using-git-well/][The Communicative Value of Using Git Well]] - ;; Ensure we have a clear buffer, and enter some text. (Namely, Python code). - (erase-buffer) - (insert "\n def first(x): pass") - (insert "\n def fierce(a, b): pass") +** Dropbox & Mega :Disabled: +:PROPERTIES: +:CUSTOM_ID: COMMENT-Dropbox-Mega-Not-really-in-use +:header-args: :tangle no +:END: +Install quick file sharing utility ---[[https://www.dropbox.com/install][Dropbox]]--- as well as a backup +service, [[https://mega.nz/fm][Mega]] ---I use this for my =backups= directory: Keep reading. +# +begin_src shell +# brew install --cask dropbox +# brew install --cask megasync +# +end_src +#+begin_src emacs-lisp +(system-packages-ensure "dropbox") +(system-packages-ensure "megasync") +#+end_src - ;; Completion anything mode is enabled by default. - (should company-mode) +** Undo-tree: Very Local Version Control +:PROPERTIES: +:CUSTOM_ID: Undo-tree-Very-Local-Version-Control +:END: + doc:undo-tree-visualize, kbd:C-x_u, gives a /visual/ representation of the + current buffer's edit history. +#+BEGIN_SRC emacs-lisp :noweb yes :tangle no + <> +#+END_SRC - ;; There are 2 completion candidates: The two we wrote. - (insert "\n fi") - (company-manual-begin) ;; i.e., C-/ - (should (equal company-candidates '("fierce" "first"))) +( We're just showing the ~<>~ from earlier since this is a good +place for such a setup. More importantly, we are /not/ copy-pasting the setup: /It +is written only once; in a single source of truth!/ ) - ;; [fi ↦ fierce] Default option is the most recently matching written word. - (insert "\n fi") - (execute-kbd-macro (kbd "C-/ ")) - (should (looking-back "fierce")) +#+begin_src emacs-lisp +;; By default C-z is suspend-frame, i.e., minimise, which I seldom use. +(global-set-key (kbd "C-z") + (lambda () (interactive) + (undo-tree-mode) ;; Ensure the mode is on + (undo-tree-visualize))) +#+end_src - ;; [fi ↦ first] We can use M-2 to select the candidate “first” - (insert "\n fi") - (execute-kbd-macro (kbd "C-/ M-2")) - (should (looking-back "first")) +** Automatic Backups +:PROPERTIES: +:CUSTOM_ID: Automatic-Backups +:END: - (kill-buffer)) +By default, Emacs saves backup files ---those ending in =~=--- in the current +directory, thereby cluttering it up. Let's place them in ~~/.emacs.d/backups~, in +case we need to look for a backup; moreover, let's keep old versions since +there's disk space to go around ---what am I going to do with 500gigs when nearly +all my ‘software’ is textfiles interpreted within Emacs 😼 -;; Let's enter Python mode and see how things change +#+BEGIN_SRC emacs-lisp +;; New location for backups. +(setq backup-directory-alist '(("." . "~/.emacs.d/backups"))) -(ert-deftest company-shows-keywords-alongside-completions-alphabetically () - :tags '(company) - (switch-to-buffer "*TESTING COMPANY MODE ~ Python*") - (python-mode) +;; Silently delete execess backup versions +(setq delete-old-versions t) - ;; Ensure we have a clear buffer, and enter some text. (Namely, Python code). - (erase-buffer) - (insert "\n def first(x): pass") - (insert "\n def fierce(a, b): pass") +;; Only keep the last 1000 backups of a file. +(setq kept-old-versions 1000) - ;; There are 3 completion candidates: The two we wrote, & the third being a Python keyword. - (insert "\n fi") - (company-manual-begin) - (should (equal company-candidates '("fierce" "first" #("finally" 0 7 (company-backend company-keywords))))) +;; Even version controlled files get to be backed up. +(setq vc-make-backup-files t) - ;; Candidates are shown alphabetically: M-2 yields “finally”. - (execute-kbd-macro (kbd "C-g C-/ M-2")) ;; Quit and restart the completion, to get to starting position, then M-2. - (should (looking-back "finally")) +;; Use version numbers for backup files. +(setq version-control t) +#+END_SRC - (kill-buffer)) +Why backups? Sometimes I may forget to submit a file, or edit, to my +version control system, and it'd be nice to be able to see a local +automatic backup. Whenever ‘I need space,’ then I simply empty +the backup directory, if ever. That the backups are numbered is so sweet ^_^ -#+end_src +Like package installations, my backups are not kept in any version control +system, like git; only locally. -We do ~M-: (ert '(tag company))~ to run these specific tests; better would be to -call these (and all) tests with a nifty name: +Finally, let's not create ~.#~ files (crashes 'npm') nor bother confirming killing processes. #+begin_src emacs-lisp -(defun my/ensure-machine-works-as-expected () - "Run all my personal tests to ensure Emacs behaves as I expect it to." - (interactive) - (load-file "init-test.el") - (ert t) - (ert-results-pop-to-timings)) +(setq confirm-kill-processes nil + create-lockfiles nil) #+end_src -Anyhow, besides boring word completion, let's add support for [[https://github.com/dunn/company-emoji][emojis]]. -#+begin_src emacs-lisp -(use-package company-emoji - :config (add-to-list 'company-backends 'company-emoji)) -#+end_src - -For example: 🥞 💻 🐵 ✉️😉 🐬 🌵. - -➡️On a new line, write ~:~ then any letter to have a tool-tip appear. -All emoji names are lowercase. ◀ +*** What changed? ---Walking through backups +:PROPERTIES: +:CUSTOM_ID: What-changed +:END: -+ On MacOS, ~C-⌘-SPC~ brings up an emoji picker, where one drags desired emojis to - textual areas. -+ [[https://gist.github.com/rxaviers/7360908][Here]] is a list of emoji ---all supported by Github. +Let's use an elementary diff system for backups: ~backup-walker~ essentially makes +all our backups behave as if they were (implicitly) version controlled. -The libraries ~emojify~ and ~emojify-logos~ provides cool items like =:haskell: -:emacs: :org: :ruby: :python:=. Unfortunately they do not easily export to html -with org-mode, so I'm not using them. -:Alternatives: #+BEGIN_SRC emacs-lisp -(use-package emojify - :config (setq emojify-display-style 'image) - :init (global-emojify-mode 1)) ;; Will install missing images, if need be. +(use-package backup-walker + :commands backup-walker-start) #+END_SRC -(use-package emojify-logos -:after emojify) - -For example, -#+begin_src emacs-lisp :tangle no - :emacs: :haskell: :org: :c: :wink: :ruby: :python: :c: :html5: -#+end_src -Yields :emacs: :haskell: :org: :c: :wink: :ruby: :python: :c: :html5:. Run ~(emojify-insert-emoji)~ to see more. +In a buffer that corresponds to a file, invoke doc:backup-walker-start to see a +visual diff of changes /between/ versions; then ~n~ and ~p~ to move between diffs. By +default, you see the changes ‘backwards’: Red means delete these things to get +to the older version; i.e., the red ‘-’ are newer items. -Rather than remember all of these, let's install [[https://github.com/syohex/emacs-ac-emoji][ac-emoji]] -for completion of emoji names. +There is also doc:diff-backup for comparing a file with its backup. -(use-package ac-emoji - :init (ac-emoji-setup) -) -:End: +# To create backups explicitly use save-buffer (C-x C-s) with prefix arguments. +# +# =diff-backup= compares a file with its backup or vice versa. +# But there is no function to restore backup files. -*** Intro to Snippets +*** Save ≈ Backup :PROPERTIES: -:CUSTOM_ID: Intro-to-Snippets +:CUSTOM_ID: Save-Backup :END: -A *snippet, template, mechanism* is a tool that when you press some keystrokes -inserts some text, possibly with some fields (‘blanks’) to fill in. Possibly -interesting read: -+ [[https://cupfullofcode.com/blog/2013/02/26/snippet-expansion-with-yasnippet/index.html][Snippet Expansion With Yasnippet: Save Yourself Keystrokes and Headaches]] - ---a nice before introduction to Yasnippet (“Yet another snippet”) -+ [[https://jpace.wordpress.com/2012/10/20/tweaking-emacs-snippets/][Tweaking Emacs: Snippets]] ---a brief article on using snippets for uniformity - across languages and to mitigate verbosity of weak languages (i.e., those - without macros). +Emacs only makes a backup the very first time a buffer is saved; I'd prefer +Emacs makes backups everytime I save! ---If I saved, that means I'm at an +important checkpoint, so please check what I have so far as a backup! +#+BEGIN_SRC emacs-lisp +;; Make Emacs backup everytime I save --------------------------------------------------------------------------------- +(defun my/force-backup-of-buffer () + "Lie to Emacs, telling it the curent buffer has yet to be backed up." + (setq buffer-backed-up nil)) -[[http://joaotavora.github.io/yasnippet/snippet-development.html][Yasnippet]] is a pleasant utility for template expansion with the alluring -feature to allow arbitrary Lisp code to be executed during expansion. -The declaration of templates is verbose, requiring a particular file -hierarchy, as such I utilise [[https://github.com/Kungsgeten/yankpad][Yankpad]] which allows me to employ -an Org-mode approach: Each template corresponds to an org heading of -the form ~Key:Words:For:Expansion:Here: name of snippet here~ and the -template body is then the body of the org heading. -Any of ~Key, Words, For, Expansion, Here~ will rewrite into the body -of the org tree. This is much more terse, and I even don't bother -with that; instead preferring to tangle my templates using yankpad -as a mere interface. It is important to note that Yankpad also provides -features that are not in Yassnippet, such as allowing arbitrary language -code to be executed ---one simply uses an org-src block! +(add-hook 'before-save-hook 'my/force-backup-of-buffer) -| [[https://www.philnewton.net/blog/exploring-emacs-yasnippet/][Here]] is a nice self-contained tutorial. | +;; [Default settings] +;; Autosave when idle for 30sec or 300 input events performed +(setq auto-save-timeout 30 + auto-save-interval 300) +#+END_SRC +# See the name of the latest backup, which is appeneded by a number for each save ;-) +# (file-newest-backup "~/.emacs.d/init.org") -There can only be one major completion backend for any mode, but -other backends can serve as secondary ones. Here's a function to -make ~company-yankpad~ a secondary of all existing backends. -#+begin_src emacs-lisp -;; Add yasnippet support for all company backends -;; -(cl-defun my/company-backend-with-yankpad (backend) - "There can only be one main completition backend, so let's - enable yasnippet/yankpad as a secondary for all completion - backends. +It is intestesting to note that the above snippet could be modified to [[https://stackoverflow.com/a/6918217/3550444][make our +own backup system]], were Emacs lacked one, by having our function simply save +copies of our file ---on each save--- where the filename is augmented with a +timestamp. - Src: https://emacs.stackexchange.com/a/10520/10352" +**   =magit= ---Emacs' porcelain interface to git +:PROPERTIES: +:CUSTOM_ID: magit-Emacs'-porcelain-interface-to-gitq +:END: - (if (and (listp backend) (member 'company-yankpad backend)) - backend - (append (if (consp backend) backend (list backend)) - '(:with company-yankpad)))) -#+end_src +*** Intro :ignore: -#+begin_src emacs-lisp -;; Yet another snippet extension program -(use-package yasnippet - :config - (yas-global-mode 1) ;; Always have this on for when using yasnippet syntax within yankpad - ;; respect the spacing in my snippet declarations - (setq yas-indent-line 'fixed)) +Let's setup an Emacs ‘porcelain’ interface to git ---it makes working with +version control tremendously convenient. +# Moreover, I add a little pop-up so that I don't forget to commit often! -;; Alternative, Org-based extension program -(use-package yankpad +(Personal reminder: If using 2FA [two factor authentication], then when you do +git operations, such as ~git push~, [[https://webkul.com/blog/github-push-with-two-factor-authentication/][you must use your PAT]] [personal access token] +/instead/ of your password! Also: Install [[https://github.com/sindresorhus/refined-github][refined-github: Browser extension that +simplifies the GitHub interface and adds useful features]]!) - :config - ;; Location of templates - (setq yankpad-file "~/.emacs.d/yankpad.org") +#+BEGIN_SRC emacs-lisp +;; Bottom of Emacs will show what branch you're on +;; and whether the local file is modified or not. +(use-package magit + :init (require 'magit-files) + :bind (("C-c M-g" . magit-file-dispatch)) + :custom ;; Do not ask about this variable when cloning. + (magit-clone-set-remote.pushDefault t)) +#+END_SRC +# :config (global-set-key (kbd "C-x g") 'magit-status) - ;; Ignore major mode, always use defaults. - ;; Yankpad will freeze if no org heading has the name of the given category. - (setq yankpad-category "Default") +Why use ~magit~ as the interface to the git version control system? In a ~magit~ +buffer nearly everything can be acted upon: Press =return=, or =space=, to see +details and =tab= to see children items, usually. - ;; Load the snippet templates ---useful after yankpad is altered - (yankpad-reload) ++ kbd:C-x_g, doc:magit-status, gives you a nice buffer with an overview + of the Git repo that you're buffer is currently visiting. ++ kbd:C-c_M-g, doc:magit-file-dispatch, lets you invoke Git actions on the + current file directly; e.g., following up with [[kbd:b]]lame, [[kbd:l]]og, [[kbd:d]]iff, + [[kbd:s]]tage, or [[kbd:c]]ommit the current file. - ;; Set company-backend as a secondary completion backend to all existing backends. - (setq company-backends (mapcar #'my/company-backend-with-yankpad company-backends))) -#+end_src + For ease, above, we have also bound this to kbd:C-c_g ---reminiscent of kbd:C-x_g :smile: -With these settings, along with the ~company~ backend, I may type a keyword then -kbd:TAB it into expansion. + #+begin_src emacs-lisp +;; When we invoke magit-status, show green/red the altered lines, with extra +;; green/red on the subparts of a line that got alerted. +(system-packages-ensure "git-delta") +(use-package magit-delta + :hook (magit-mode . magit-delta-mode)) -Yankpad requires we have an org file that contains our templates, so we /tangle/ -such a file ~~/.emacs.d/yankpad.org~, and have all of our templates be globally -accessible. Here is the start of my file: -#+BEGIN_SRC org :tangle "~/.emacs.d/yankpad.org" :comments none -,#+Description: This is file is generated from my init.org; do not edit. +;; Don't forget to copy/paste the delta config into the global ~/.gitconfig file. +;; Copy/paste this: https://github.com/dandavison/delta#get-started + #+end_src -,* Default :global: -#+end_src ++ Blame, doc:magit-blame, is super nice: The buffer gets annotations for each chunk + of text, regarding who authoured it, when, and their commit title. + Then [[kbd:q]] to quit the blame. -#+begin_details Fully discussed example: Using the clipboard for Org-links -Here's an example of a common template I perform by hand ---no more! I have the -expected habit of /copying (to clipboard)/ a URL from someplace then forming a -link to it by writing ~[[URL] [description]]~, since the URL & syntax are already -known, let's expand those and place the cursour at the only unknown ---the -description. -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none -,** my_org_insert_link: cleverly insert a link copied to clipboard - [[${1:`(clipboard-yank)`}][$2]] $0 -#+end_src + Likewise, doc:magit-log-buffer-file is super neat! -What's going on here? ( The above, verbatim: ~[[${1:`(clipboard-yank)`}][$2]] $0~. ) -0. This template is expanded with the keyword ~my-org-insert-link~, then kbd:TAB. -1. The cursour lands at position ~$1~, which has default text being the result - of evaluating ~(clipboard-yank)~. - # I've ‘documented’ this default as being the url. +#+begin_details Super Simple ‘magit’ Mini-tutorial +link-here:Super-Simple-‘magit’-Mini-tutorial +Below is my personal quick guide to working with magit ---for a full tutorial +see [[http://jr0cket.co.uk/2012/12/driving-git-with-emacs-pure-magic-with.html.html][jr0cket's blog]]. - # If I leave out the ~$(clipboard-yank)~ part, the default would simply be ~url~ pasted in. ++ ~dired~ :: See the contents of a particular directory. - We may evaluate Lisp code anywhere by enclosing it in backticks. ++ ~magit-init~ :: Put a project under version control. + The mini-buffer will prompt you for the top level folder version. + A ~.git~ folder will be created there. - # `(file-name-nondirectory (file-name-sans-extension (buffer-file-name)))` ++ ~magit-status~ , ~C-x g~ :: See status in another buffer. + Press ~?~ to see options, including: + - g :: Refresh the status buffer. + # The status buffer may be refereshed using ~g~, and all magit buffers by ~G~. + - TAB :: See collapsed items, such as what text has been changed. + - ~q~ :: Quit magit, or go to previous magit screen. + - ~s~ :: Stage, i.e., add, a file to version control. + Add all untracked files by selecting the /Untracked files/ title. -2. If we're satisfied with the current field, we simply tab to the next field. - Otherwise, we simply write text ---which overwrites the default text. + [[https://softwareengineering.stackexchange.com/a/119807/185815][The staging area is akin to a pet store; commiting is taking the pet home.]] -3. After enough tabbing we complete the template and the cursour lands - at position ~$0~. + - ~k~ :: Kill, i.e., delete a file locally. + - ~K~ :: This' ~(magit-file-untrack)~ which does ~git rm --cached~. + - ~i~ :: Add a file to the project ~.gitignore~ file. Nice stuff =) + - ~u~ :: Unstage a specfif staged change highlighed by cursor. + ~C-u s~ stages everything --tracked or not. + - ~c~ :: Commit a change. + * A new buffer for the commit message appears, you write it then + commit with ~C-c C-c~ or otherwise cancel with ~C-c C-k~. + These commands are mentioned to you in the minibuffer when you go to commit. + * You can provide a commit to /each/ altered chunk of text! + This is super neat, you make a series of local such commits rather + than one nebulous global commit for the file. The ~magit~ interface + makes this far more accessible than a standard terminal approach! + * You can look at the unstaged changes, select a /region/, using ~C-SPC~ as usual, + and commit only that if you want! + * When looking over a commit, ~M-p/n~ to efficiently go to previous or next altered sections. + * Amend a commit by pressing ~a~ on ~HEAD~. -⟪ Having default or mirrored text for ~$2~ would not allow me to see the URL -field, lest I wish to change it or at least confirm it's what I want. -Hence, the ~$2~ field has no default. ⟫ + - ~d~ :: Show differences, another ~d~ or another option. + - This is magit! Each hunk can be acted upon; e.g., ~s~ or ~c~ or ~k~ ;-) + - ~v~ :: Revert a commit. + - ~x~ :: Undo last commit. Tantamount to ~git reset HEAD~~ when cursor is on most recent + commit; otherwise resets to whatever commit is under the cursor. + - ~l~ :: Show the log, another ~l~ for current branch; other options will be displayed. + - Here ~space~ shows details in another buffer while cursour remains in current + buffer and, moreover, continuing to press ~space~ scrolls through the other buffer! + Neato. + - ~P~ :: Push. + - ~F~ :: Pull. + - ~:~ :: Execute a raw git command; e.g., enter ~whatchanged~. -Let's overwrite the usual way to insert such links, via ~C-c C-l~. -#+BEGIN_SRC emacs-lisp -(cl-defun org-insert-link () - "Makes an org link by inserting the URL copied to clipboard and - prompting for the link description only. - - Type over the shown link to change it, or tab to move to the - description field. +Notice that every time you press one of these commands, a ‘pop-up’ of realted +git options appears! Thus not only is there no need to memorise many of them, +but this approach makes /discovering/ other commands easier. - This overrides Org-mode's built-in ‘org-insert-link’ utility; - whence C-c C-l uses the snippet." - (interactive) - (insert "my_org_insert_link") - (yankpad-expand)) -#+END_SRC +# Use ~M-x magit-list-repositories RET~ to list local repositories: #+end_details -/Warning!/ Snippet names cannot have hypens in them ---in this setup at least. +#+begin_details [Disabled] Homemade ‘uncomitted changes’ Notification +Let's always notify ourselves of a file that has [[https://tpapp.github.io/post/check-uncommitted/][uncommited changes]] +---we might have had to step away from the computer and forgotten to commit. +#+begin_src emacs-lisp :tangle no +(require 'magit-git) -The [[http://joaotavora.github.io/yasnippet/snippet-development.html][Yasnippet manual]] is an accessible read, as is the [[https://github.com/Kungsgeten/yankpad][Yankpad manual]], and - showcases many other utilities; such as having certain snippets being - enabled only in particular modes or on demand. Of note is that field ~$n~ can be - accessed in code with the invocation ~(yas-field-value n)~. +(defun my/magit-check-file-and-popup () + "If the file is version controlled with git + and has uncommitted changes, open the magit status popup." + (let ((file (buffer-file-name))) + (when (and file (magit-anything-modified-p t file)) + (message "This file has uncommited changes!") + (when nil ;; Became annyoying after some time. + (split-window-below) + (other-window 1) + (magit-status))))) -Incidentally, I used this snippet setup to [[https://www.youtube.com/watch?v=NYOOF9xKBz8&feature=youtu.be][demo]] the idea of repetitious code in -grouping constructs within dependently-typed languages, which was accepted and -led to my doctoral research on a [[https://alhassy.github.io/next-700-module-systems/][‘do it yourself module system’]]. +;; I usually have local variables, so I want the message to show +;; after the locals have been loaded. +(add-hook 'find-file-hook + '(lambda () + (add-hook 'hack-local-variables-hook 'my/magit-check-file-and-popup))) +#+end_src +:Lets_try_this_out: +#+BEGIN_EXAMPLE emacs-lisp +(progn (eshell-command "echo change-here >> ~/dotfiles/.emacs") + (find-file "~/dotfiles/.emacs")) +#+END_EXAMPLE +:End: +# In doubt, execute ~C-h e~ to jump to the ~*Messages*~ buffer. +#+end_details -The rest of this section is other templates, not much for now, -concluding with actually loading this snippet mechanism globally. +*** Credentials: I am who I am +:PROPERTIES: +:CUSTOM_ID: Credentials-I-am-who-I-am +:END: +First, let's setup our git credentials. +#+BEGIN_SRC emacs-lisp +;; Only set these creds up if there is no Git email set up ---ie at work I have an email set up, so don't +;; override it with my personal creds. +;; +;; See here for a short & useful tutorial: +;; https://alvinalexander.com/git/git-show-change-username-email-address +(when (equal "" (shell-command-to-string "git config user.email ")) + (shell-command (format "git config --global user.name \"%s\"" user-full-name)) + (shell-command (format "git config --global user.email \"%s\"" user-mail-address))) +#+END_SRC - The remaining subsections discuss contents of my yankpad file. +If we ever need to use Git in the terminal, it should be done with Emacs as the +underlying editor +#+BEGIN_SRC emacs-lisp +;; We want to reuse an existing Emacs process from the command line +;; E.g., emacsclient --eval '(+ 1 2)' # ⇒ 3 +(server-start) -*** Org-mode Templates ---A reason I “generate” templates ;) +;; Or use it whenever we are editing a git message from the terminal +(shell-command "git config --global core.editor 'emacsclient -t -a=\\\"\\\"'") +#+END_SRC +This will start a daemon if there is not already one running ---The ~-a~ option--- +and opens a new Emacs frame on the current terminal ---The ~-t~ option. + +*** Encouraging useful commit messages :PROPERTIES: -:CUSTOM_ID: Org-mode-Templates-A-reason-I-generate-templates +:CUSTOM_ID: Encouraging-useful-commit-messages :END: +Let's try our best to have a [[https://chris.beams.io/posts/git-commit/][useful & consistent commit log]]: +#+begin_src emacs-lisp +(defun my/git-commit-reminder () + (insert "\n\n# The commit subject line ought to finish the phrase: +# “If applied, this commit will ⟪your subject line here⟫.” ") + (beginning-of-buffer)) -This produces a pop-up list of org-mode block types, if ~src~ is selected, then a -list of my commonly used languages pops-up. Alternatively, ignore the pop-up -menu and write any block or language name. -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none -,** begin: produce an org-mode block -,#+begin_${1:environment$(let* - ((block '("src" "example" "quote" "verse" "center" "latex" "html" "ascii")) - (langs '("c" "emacs-lisp" "lisp" "latex" "python" "sh" "haskell" "plantuml" "prolog")) - (type (yas-choose-value block))) - (concat type (when (equal type "src") (concat " " (yas-choose-value langs)))))} - $0 -,#+end_${1:$(car (split-string yas-text))} +(add-hook 'git-commit-setup-hook 'my/git-commit-reminder) #+end_src -In this case, ~yas-text~ is equivalent to (~yas-field-value 1)~; -it generally refers to the value of the field being mirrored with ~${n: ⋯yas-text⋯}~. -However, going through pop-ups takes precious time ---besides being slightly annyoing. -Let's introduce a template for my most utilised kind of language blocks. -#+begin_example -,** s_org: src block for org -,#+begin_src org -$0 -,#+end_src -#+end_example +Super neat stuff! -However, doing this for each language I want is a waste of time and textual -space. Why? *The purpose of templates is to reduce repetition,* yet the above -block would be repeated with only 3 parts ‘unknown’: The expansion keyword, the -description, and the org-mode source block name. Whence, the template /text/ is -generated by the following basic loop ---whose source block is named -~my-org-lang-templates~. -#+name: my-org-lang-templates -#+begin_src emacs-lisp :tangle no :wrap "src org :tangle ~/.emacs.d/yankpad.org" :exports code :results replace drawer -;; We make an org BLOCK snippet template for each LANG the user has declared. -;; -(cl-loop for (shortcut block takes-language-argument? default-text) - in '(("s_" "src" t) - ("is_" "inline source" t) ;; Treated specially below - ("e_" "example" t) - ("q_" "quote") - ("v_" "verse") - ("c_" "center") - ("ex_" "export") ;; only HTML and LATEX - ;; https://alhassy.github.io/org-special-block-extras/#Summary - ("p_" "parallel" nil "\n$0\n#+columnbreak:\n") - ("d_" "details" nil "${1:title}\n$0") - ("ed_" "edcomm" nil "${1:editor}\n$0") - ("doc_" "documentation" nil "${1: mandatory entry name}\n$0") - ("def_" "latex-definitions")) - for languages = (if takes-language-argument? - (-cons* "org" "agda2" "any" ;; Extra ‘languages’ - ;; Also include whatever languages we've loaded for literate programming. - (--map (symbol-name (car it)) org-babel-load-languages)) - '("")) ;; The “empty language” - concat (cl-loop for lang in languages - for key = (concat shortcut - (if (s-blank? lang) block lang)) - for description = (if (s-blank? lang) - block - (concat - block " for " lang)) - concat (if (equal "is_" shortcut) - (concat "\n** " key ": " description - "\nsrc_" lang "[:exports code]{$1} $0") - (concat "\n** " key ": " description - "\n#+begin_" block " " lang - (or default-text "\n$0") - "\n#+end_" block "\n")))) -#+end_src +*** Maybe clone ... everything? +:PROPERTIES: +:CUSTOM_ID: Maybe-clone-everything +:END: -The /resulting text/ of this block, generated below, is tangled to our yankpad by -utilising a [[https://www.gnu.org/software/emacs/manual/html_node/org/Noweb-reference-syntax.html][noweb]] source block invocation. An example of the resulting text is -the above ~s_org~ block. The result is (last I checked) *83* template expansions ----that would have been a bit much to write by hand. -#+begin_example org -,#+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes -<> -,#+end_src -#+end_example -# ActuallyDoIt -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes :comments none -<> -#+end_src +Below are the git repos I'd like to clone ---along with a function to do so +quickly. +#+begin_src emacs-lisp +;; Clone git repo from clipboard +(cl-defun maybe-clone (remote &optional local) + "Clone a REMOTE repository [from clipboard] if the LOCAL directory does not exist. -# The “:eval never-export” means that this block is never tangled on document -# export, C-c C-e. +If called interactively, clone URL in clipboard into ~/Downloads then open in dired. -#+begin_box -Now ~s_~, due to company mode, brings up a list of languages that I can then -scroll down through, then “enter” upon to expand. Moreover, the prefix ~s_~ means -that the key is mostly irrelevant, since I needn't remember it because -company-mode immediately lists possible completions /along/ with the /descriptions/ -for the snippets. Likewise for examples with ~e_~ or quotes with ~q_~. Super neat -stuff :-) +Yields ‘repo-already-exists’ when no cloning transpires, otherwise yields ‘cloned-repo’. - Ain't this reminiscent of meta-programming ;-) -#+end_box -Using =noweb= invocations, any time the tangling is performed, the yankpad -is kept up to date ---no personal intervention from myself. +LOCAL is optional and defaults to the base name; e.g., +if REMOTE is https://github.com/X/Y then LOCAL becomes ∼/Y." + (interactive "P") -# Neat, but not what I want. -# https://github.com/abo-abo/auto-yasnippet + (when (interactive-p) + (setq remote (substring-no-properties (current-kill 0))) + (cl-assert (string-match-p "^\\(http\\|https\\|ssh\\)://" remote) nil "No URL in clipboard")) -:Fun_albeit_useless_exercise: + (unless local + (setq local (concat "~/" (if (interactive-p) "Downloads/" "") (file-name-base remote)))) -Let's push this frontier a bit more … + ;; (require 'magit-repos) ;; Gets us the magit-repository-directories variable. + ;; (add-to-list 'magit-repository-directories `(,local . 0)) -In expressive languages like Agda, one can not only be type polymorphic but also -‘level polymorphic’ ---since types constitute a hierarchy where a ‘type’ is -uninterestingly an ‘element’ of a higher ‘type’, ad infinitum. For example, the -type of a level polymorphic ′choice’ function would be ~{a : Level} (A : Set a) → A~ ----note that such a choice function cannot exist since for any type ~A~ it returns -an element of ~A~, then what of the empty type. Anyhow, the template ~{${1:a} : -Level} → (${2:A} : Set $1) → $0~ would suffice to make this happen. Yet, what if -we wanted /n/-many sets? + (if (file-directory-p local) + 'repo-already-exists + (shell-command (concat "git clone " remote " " local)) + (dired local) + 'cloned-repo)) -Make a function that takes /n ≤ 26/ as input, produces a list of -levels, then uses each level to produce a list of type names ;-) -:End: -With the advent of org-special-block-extras, I've made increased usage of links ---such as ~green:hello~ which yields green:hello and ~[[kbd:][green]]~ which yields -[[kbd:][green]]. -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none -,** ll_make_a_link: insert a link template -${1:`(let* ((τ (read-string "Link type: ")) - (δ (read-string "Link Description: ")) - (⊤ (if (s-contains? ":" τ) τ (s-concat τ ":")))) - (format "[[%s][%s]]" ⊤ δ))`} $0 -#+END_SRC +(maybe-clone "https://github.com/alhassy/emacs.d" "~/.emacs.d") +(maybe-clone "https://github.com/alhassy/alhassy.github.io" "~/blog") +(maybe-clone "https://github.com/alhassy/holy-books") +#+end_src -*** Operating System Keyboard Symbols -:PROPERTIES: -:CUSTOM_ID: Operating-System-Keyboard-Symbols -:END: +#+begin_details Many more repos to clone +#+begin_src emacs-lisp -Write ~os_~ then see a bunch of completions ;-) +(maybe-clone "https://github.com/alhassy/melpa") +(maybe-clone "https://github.com/alhassy/org-special-block-extras") -#+name: my-snippets-os-symbols -#+begin_src emacs-lisp :tangle no :wrap "src org :tangle ~/.emacs.d/yankpad.org" :exports code :results replace drawer -(cl-loop for (name expansion) - in '((os-command ⌘) - (os_option ⌥) - (os_alt ⌥) - (os_control ⌃) - (os_shift ⇧) - (os_backspace ⌫) - (os_delete ⌫) - (os_delete_forward ⌦) - (os_enter ⏎) - (os_return ⏎) - (os_escape ⎋) - (os_tab_right ⇥) - (os_tab_left ⇤) - (os_caps_lock ⇪) - (os_eject ⏏)) - concat - (format "** %s: %s Operating System Keyboard Symbol\n%s\n" name expansion expansion)) -#+end_src -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes :comments none -<> -#+end_src +;; (maybe-clone "https://github.com/alhassy/next-700-module-systems-proposal.git" "~/thesis-proposal") +;; (maybe-clone "https://github.com/JacquesCarette/MathScheme") +;; (maybe-clone "https://github.com/alhassy/gentle-intro-to-reflection" "~/reflection/") +;; (maybe-clone "https://github.com/alhassy/org-agda-mode") +;; (maybe-clone "https://github.com/JacquesCarette/TheoriesAndDataStructures") +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/RATH/RATH-Agda" "~/RATH-Agda") +;; (maybe-clone "https://github.com/alhassy/MyUnicodeSymbols") ;; Deleted? +(maybe-clone "https://github.com/alhassy/islam") +(maybe-clone "https://github.com/alhassy/CheatSheet") +(maybe-clone "https://github.com/alhassy/ElispCheatSheet") +;; (maybe-clone "https://github.com/alhassy/CatsCheatSheet") +;; (maybe-clone "https://github.com/alhassy/OCamlCheatSheet") +;; (maybe-clone "https://github.com/alhassy/AgdaCheatSheet") +(maybe-clone "https://github.com/alhassy/RubyCheatSheet") +;; (maybe-clone "https://github.com/alhassy/PrologCheatSheet") +;; (maybe-clone "https://github.com/alhassy/FSharpCheatSheet") -*** Work Templates -:PROPERTIES: -:CUSTOM_ID: Work-Templates -:END: -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none -,** ll_console_log: Log some JS variables +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/armstmp/cs3mi3.git" "~/3mi3") +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/CAS781" "~/cas781") ;; cat adventures +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/carette/cs3fp3.git" "~/3fp3") +;; (maybe-clone "https://github.com/alhassy/interactive-way-to-c") +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-distribution.git" "~/3ea3/assignment-distribution") +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/notes.git" "~/3ea3/notes") +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/assignment-development.git" "~/3ea3/assignment-development") +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/kandeeps.git" "~/3ea3/sujan") +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/horsmane.git" "~/3ea3/emily") +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/3ea3-winter2019/anderj12.git" "~/3ea3/jacob") +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/alhassm/3EA3.git" "~/3ea3/_2018") +;; (maybe-clone "https://gitlab.cas.mcmaster.ca/2DM3/LectureNotes.git" "~/2dm3") +#+end_src +#+end_details -console.log("%c ******* LOOK HERE *******", "color: green; font-weight: bold;"); -console.log({ ${1:List the variables here whose values you want to log} }); -$0 +This doc:maybe-clone utility has genuinely +made it easier for me to learn about new projects and codebases from Github: +I type it in with the repo's address, then kbd:C-x_C-e ---doc:eval-last-sexp--- +and then I can view it in my beloved Emacs ~(─‿‿─)~. -,** uuidgen: Insert the result of “uuidgen” and copy it to the clipboard +Moreover, this handy tool makes it so that you can +list your Git repositories with doc:magit-list-repositories: +It marks modified repos as “red:dirty”. -${1:`(-let [it (shell-command-to-string "uuidgen | tr '[:upper:]' '[:lower:]' | -pbcopy; pbpaste")] (message "Copied to clipboard, uuid: %s" it) it)`} -#+end_src +It may be useful to know that ~(magit-anything-modified-p t file)~ can be used to +check if ~file~ has been modified (doc:magit-anything-modified-p), whereas +~(magit-status repo)~ checks the status of a repository (doc:magit-status). +# (magit-anything-modified-p t "~/ElispCheatSheet/CheatSheet.org") +# (magit-status "~/ElispCheatSheet") -*** Elisp Templates +*** Gotta love that time machine :PROPERTIES: -:CUSTOM_ID: Elisp-Templates +:CUSTOM_ID: Gotta-love-that-time-machine :END: -The following snippets were rather useful as I began learning Lisp to construct -my editor of choice ---I love Emacs so much. Admittedly, I still need the first -one below and usually beat around the bush by using ~(cl-loop for ⋯ do ⋯)~, (doc:cl-loop), which is -‘noisier’ but easier to remember and to read for non-Lispers. -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none -,** loop: Elisp's for each loop -(dolist (${1:var} ${2:list-form}) - ${3:body}) +Finally, one of the main points for using version control is to have access to +historic versions of a file. The following utility allows us to +kbd:M-x_git-timemachine on a file and use kbd:p/n/g/q to look at previous, next, +goto arbitrary historic versions, or quit. +#+BEGIN_SRC emacs-lisp +(use-package git-timemachine ) +#+END_SRC +If we want to roll back to a previous version, we just doc:write-file or +kbd:C-x_C-s as usual! The power of text! -,** defun: Lisp functions -(cl-defun ${1:fun-name} (${2:arguments}) - "${3:documentation}" - $0) +-------------------------------------------------------------------------------- -,** cond: Elisp conditionals -(cond (${1:scenario₁} ${2:response₁}) - (${3:scenario₂} ${4:response₂})) -#+end_src +doc:vc-annotate is also very useful to go through history and work out when +things went wrong. -*** Equational Templates +*** Jump to a (ma)git repository with ~C-u C-x g~ :PROPERTIES: -:CUSTOM_ID: Equational-Templates +:CUSTOM_ID: Jump-to-a-ma-git-repository-with-C-u-C-x-g :END: -To show ~ℒ = ℛ~, one starts at the complicated side, say /ℒ/, then, with the aim of -simplification, tries to end at the simpler side, /𝓡/. Along the way, one -justifies each step of the calculation. This approach is popular in the proof -assistant Agda; [[https://alhassy.github.io/PathCat/][Examples]]. Read more about [[http://www.mathmeth.com/][informal calculational proofs]]. - -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none -,** fun: Function declaration with type signature - -${1:fun-name} : ${2:arguments} -$1 ${3:args} = ?$0 - -,** eqn_begin: Start a ≡-Reasoning block in Agda - -begin - ${1:complicated-side} -$0≡⟨ ${3:reason-for-the-equality} ⟩ - ${2:simpler-side} -∎ +#+begin_src emacs-lisp +;; Jump to a (ma)git repository with C-u C-x g. +;; +;; To get a selection of repositories (that have been visited at least once), +;; call with “C-u M-x magit-status” or “C-u C-x g”; use “C-u C-u C-x g” to +;; manually enter a path to a repository. +;; +;; We use projectile's record of known projects, and keep only projects with +;; .git directory. +(with-eval-after-load 'projectile + (setq magit-repository-directories + (thread-last (projectile-relevant-known-projects) + (--filter (unless (file-remote-p it) + (file-directory-p (concat it "/.git/")))) + (--map (list (substring it 0 -1) 0))))) -,** eqn_step: Insert a step in a ≡-Reasoning block in Agda -≡⟨ ${2:reason-for-the-equality} ⟩ - ${1:new-expression} -$0 +;; Follow-up utility +(defun my/update-repos () + "Update (git checkout main & pull) recently visited repositories." + (interactive) + (cl-loop for (repo _depth) in magit-repository-directories + ;; Is it “main” or “master” + for trunk = (s-trim (shell-command-to-string (format "cd %s; git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'" repo))) + do (message (format "🤖 %s ∷ Checking out & pulling main" repo)) + (shell-command (format "cd %s; git checkout %s; git pull" repo trunk))) + (message "🥳 Happy coding!")) #+end_src -One expands ~eqn_begin~, tabs to fill in the three main locations, then -/immediately/ types ~eqn_step~ to produce a new step in a calculational proof. - -*** Fixed replies +** Github /within/ Emacs :Disabled: :PROPERTIES: -:CUSTOM_ID: Fixed-replies +:CUSTOM_ID: Github-within-Emacs +:header-args: :tangle no :END: -Here are some replies that I sometimes need to produce; e.g., to people who -insist their way is the right way. +We can work with Github/Gitlab/etc from the comfort of Emacs Magit using the [[https://github.com/magit/forge][forge]] package. ++ In particular, it adds two new “topics” to the Magit status page: =Issues= and =Pull Requests=. + - We can tab on these to see their “posts” ---the contributions to the conversations and /new/ commits. + - [[https://magit.vc/manual/forge/Editing-Topics-and-Posts.html][Here]] is a list of actions that can be done on topics and posts. -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none -,** reply_opinionated_pantomath: What to say to, e.g., an arrogant academic +#+begin_src emacs-lisp +(use-package forge + :after magit) +#+end_src -Your certainty inspires me to continuing exploring, and I may arrive at your -point of view, but I'm going to need more evidence first. +1. Make a file =~/.authinfo= whose top-most line is =machine api.github.com login ⟪𝑼𝑺𝑬𝑹𝑵𝑨𝑴𝑬⟫^forge password ⟪𝑻𝑶𝑲𝑬𝑵⟫= where ⟪⋯⟫ + should be replaced by your Github username and a token ---which can be [[https://github.com/settings/tokens][created from this page]] with [[https://magit.vc/manual/forge/Token-Creation.html][permissions]] for + ~repo, user, read:org~ checked-off. + - For more detailed information, see [[https://magit.vc/manual/ghub/Creating-and-Storing-a-Token.html][this Forge manual page]]. -,** reply_em_dashes: Why use em dashes for parenthetical remarks? +2. Now open a Magit status buffer for a repository and run ~M-x forge-pull~, after which you should be able to see all issues and pull requests. + - Alternatively, press ~N~ to see the forge menu. + - Press ~N f f~ to pull latest information from the repository. + + Note how pulling data from a forge’s API works the same way as pulling Git data does; you do it explicitly when + you want to see the work done by others. + + We can also “c”reate a new “p”ull request or a new “i”ssue with ~N c p~ or ~N c i~, respectively. + + [[https://www.youtube.com/watch?v=fFuf3hExF5w&ab_channel=ZaisteProgramming][~5 min video]] -According to the “Canadian Style Guide” (CSG): +To *review a pull request* press tab on ~Pull Requests~ then press ~b y~ to checkout that branch locally. - The em is an expansive, attention-seeking dash. It supplies much stronger - emphasis than the comma, colon or semicolon it often replaces. Positioned - around interrupting elements, em dashes have the opposite effect of - parentheses—em dashes emphasize; parentheses minimize. +** Pretty Magit Commit Leaders +:PROPERTIES: +:CUSTOM_ID: Pretty-Magit-Commit-Leaders +:END: +⟨ Following [[http://www.modernemacs.com/post/pretty-magit/][Pretty Magit - Integrating commit leaders | Modern Emacs]] ⨾⨾ Code +comes from there as well. Notable alteration: Helm compleition shows +description of leaders. ⟩ -From “A Logical Approach to Discrete Math” (LADM), page ix: +Add faces to Magit to achieve icon and colored commit leaders. I also integrate +Helm to prompt a leader when committing so there's no need to remember or type +out completely every leader we choose. +- /It's not just aesthetics. It's about visual clarity./ +- [[https://www.reddit.com/r/emacs/comments/6jegis/comment/djeo89w/?utm_source=share&utm_medium=web2x&context=3][Here]] is an alternate approach: Add icons based on words mentioned in commit titles ---no leaders required. - We place a space on one side of an em dash ---here are examples--- in - order to help the reader determine whether the em dash begins or ends - a parenthetical remark. In effect, we are creating two symbols from one. - In longer sentences---and we do write long sentences from time to time---the - lack of space can make it difficult to see the sentence structure---especially - if the em dash is used too often in one sentence. Parenthetical remarks - delimited by parentheses (like this one) have a space on one side of each - parenthesis, so why not parenthetical remarks delimited by em dashes? +#+begin_details "Boring details ~ See linked article instead" +#+begin_src emacs-lisp +(cl-defmacro pretty-magit (WORD ICON PROPS &optional (description "") NO-PROMPT?) + "Replace sanitized WORD with ICON, PROPS and by default add to prompts." + `(prog1 + (add-to-list 'pretty-magit-alist + (list (rx bow (group ,WORD (eval (if ,NO-PROMPT? "" ":")))) + ,ICON ',PROPS)) + (unless ,NO-PROMPT? + (add-to-list 'pretty-magit-prompt (cons (concat ,WORD ": ") ,description))))) -Interestingly, according to the CSG, there should be no space before or after an -em dash. As such, it appears that the spacing is mostly stylistic; e.g., some -people surround em-s with spaces on both sides. In particular, when em-s are -unmatched, I make no use of additional space ---indeed this form of one-sided -parentheses without a space is how LADM is written, as can be seen at the top of -page 3. +(setq pretty-magit-alist nil) +(setq pretty-magit-prompt nil) #+end_src +#+end_details -*** COMMENT Misc Templates -:PROPERTIES: -:CUSTOM_ID: Misc-Templates -:END: - -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none -,** remark: top-level literate comment - -{{{remark(${1:thoughts})}}} $0 +My personal choices for leaders are: +#+begin_src emacs-lisp +(pretty-magit "Add" ?➕ (:foreground "#375E97" :height 1.2) "✅ Create a capability e.g. feature, test, dependency.") +(pretty-magit "Delete" ?❌ (:foreground "#375E97" :height 1.2) "❌ Remove a capability e.g. feature, test, dependency.") +(pretty-magit "Fix" ?🔨 (:foreground "#FB6542" :height 1.2) "🐛 Fix an issue e.g. bug, typo, accident, misstatement.") +(pretty-magit "Clean" ?🧹 (:foreground "#FFBB00" :height 1.2) "✂ Refactor code; reformat say by altering whitespace; refactor performance.") +(pretty-magit "Document" ?📚 (:foreground "#3F681C" :height 1.2) "ℹ Refactor of documentation, e.g. help files.") +(pretty-magit "Feature" ?⛲ (:foreground "slate gray" :height 1.2) "⛳ 🇮🇶🇨🇦 A milestone commit - flagpost") +(pretty-magit "Generate" ?🔭 (:foreground "slate gray" :height 1.2) "Export PDF/HTML or tangle raw code from a literate program") ;; Generating artefacts +(pretty-magit "master" ? (:box t :height 1.2) "" t) +(pretty-magit "origin" ?🐙 (:box t :height 1.2) "" t) +;; Commit leader examples: https://news.ycombinator.com/item?id=13889155. +;; +;; Cut ~ Remove a capability e.g. feature, test, dependency. +;; Bump ~ Increase the version of something e.g. dependency. +;; Make ~ Change the build process, or tooling, or infra. +;; Start ~ Begin doing something; e.g. create a feature flag. +;; Stop ~ End doing something; e.g. remove a feature flag. #+end_src -Where my local use contains ~#+MACRO: remark @@latex: \fbox{\textbf{Comment: $1 }}@@~. - -*** Emojis -:PROPERTIES: -:CUSTOM_ID: -:END: - -#+NAME: my-emoji-templates -#+begin_src emacs-lisp :tangle no :wrap "src org :tangle ~/.emacs.d/yankpad.org" :exports code :results replace drawer -;; -;; https://emojipedia.org/people/ -(cl-loop for (emoji name description) - in '((😀 "Grinning Face" - "Often conveys general pleasure and good cheer or humor.") - - (😃 "Grinning Face with Big Eyes" - "Often conveys general happiness and good-natured amusement. - Similar to 😀 Grinning Face but with taller, - more excited eyes.") - - (😄 "Grinning Face with Smiling Eyes" - "Often conveys general happiness and good-natured amusement. - Similar to 😀 Grinning Face and 😃 Grinning - Face With Big Eyes, but with warmer, less - excited eyes.") +#+begin_details "Boring details ~ See linked article instead" +#+begin_src emacs-lisp +(defun add-magit-faces () + "Add face properties and compose symbols for buffer from pretty-magit." + (interactive) + (with-silent-modifications + (--each pretty-magit-alist + (-let (((rgx icon props) it)) + (save-excursion + (goto-char (point-min)) + (while (search-forward-regexp rgx nil t) + (compose-region + (match-beginning 1) (match-end 1) icon) + (when props + (add-face-text-property + (match-beginning 1) (match-end 1) props)))))))) - (😁 "Beaming Face with Smiling Eyes" - "Often expresses a radiant, gratified - happiness. Tone varies, including warm, silly, - amused, or proud.") +(advice-add 'magit-status :after 'add-magit-faces) +(advice-add 'magit-refresh-buffer :after 'add-magit-faces) - (😆 "Grinning Squinting Face" - "Often conveys excitement or hearty laughter. - Similar to 😀 Grinning Face but with eyes that - might say ‘Squee!’ or ‘Awesome!’ An emoji form of - the >< or xD emoticons.") - (😅 "Grinning Face with Sweat" - "Intended to depict nerves or discomfort but - commonly used to express a close call, as if - saying ‘Whew!’ and wiping sweat from the - forehead. ") +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - (🤣 "Rolling on the Floor Laughing" - "Often conveys hysterical laughter more intense - than 😂 Face With Tears of Joy.") +(setq use-magit-commit-prompt-p nil) +(defun use-magit-commit-prompt (&rest args) + (setq use-magit-commit-prompt-p t)) - (😂 "Face with Tears of Joy") - (🙂 "Slightly Smiling Face") - (🙃 "Upside-Down Face") - (😉 "Winking Face") - (😊 "Smiling Face with Smiling Eyes") - (😇 "Smiling Face with Halo") - (🥰 "Smiling Face with Hearts") - (😍 "Smiling Face with Heart-Eyes") - (🤩 "Star-Struck") - (😘 "Face Blowing a Kiss") - (😗 "Kissing Face") - (☺️ "Smiling Face") - (😚 "Kissing Face with Closed Eyes") - (😙 "Kissing Face with Smiling Eyes") - (🥲 "Smiling Face with Tear") - (😋 "Face Savoring Food") - (😛 "Face with Tongue") - (😜 "Winking Face with Tongue") - (🤪 "Zany Face") - (😝 "Squinting Face with Tongue") - (🤑 "Money-Mouth Face") - (🤗 "Hugging Face") - (🤭 "Face with Hand Over Mouth") - (🤫 "Shushing Face") - (🤔 "Thinking Face") - (🤐 "Zipper-Mouth Face") - (🤨 "Face with Raised Eyebrow") - (😐 "Neutral Face") - (😑 "Expressionless Face") - (😶 "Face Without Mouth") - (😏 "Smirking Face") - (😒 "Unamused Face") - (🙄 "Face with Rolling Eyes") - (😬 "Grimacing Face") - (🤥 "Lying Face") - (😌 "Relieved Face") - (😔 "Pensive Face") - (😪 "Sleepy Face") - (🤤 "Drooling Face") - ("Sleeping Face") - (😷 "Face with Medical Mask") - (🤒 "Face with Thermometer") - (🤕 "Face with Head-Bandage") - (🤢 "Nauseated Face") - (🤮 "Face Vomiting") - (🤧 "Sneezing Face") - (🥵 "Hot Face") - (🥶 "Cold Face") - (🥴 "Woozy Face") - (😵 "Dizzy Face") - (🤯 "Exploding Head") - (🤠 "Cowboy Hat Face") - (🥳 "Partying Face") - (🥸 "Disguised Face") - (😎 "Smiling Face with Sunglasses") - (🤓 "Nerd Face") - (🧐 "Face with Monocle") - (😕 "Confused Face") - (😟 "Worried Face") - (🙁 "Slightly Frowning Face") - (☹️ "Frowning Face") - (😮 "Face with Open Mouth") - (😯 "Hushed Face") - (😲 "Astonished Face") - (😳 "Flushed Face") - (🥺 "Pleading Face") - (😦 "Frowning Face with Open Mouth") - (😧 "Anguished Face") - (😨 "Fearful Face") - (😰 "Anxious Face with Sweat") - (😥 "Sad but Relieved Face") - (😢 "Crying Face") - (😭 "Loudly Crying Face") - (😱 "Face Screaming in Fear") - (😖 "Confounded Face") - (😣 "Persevering Face") - (😞 "Disappointed Face") - (😓 "Downcast Face with Sweat") - (😩 "Weary Face") - (😫 "Tired Face") - (🥱 "Yawning Face") - (😤 "Face with Steam From Nose") - (😡 "Pouting Face") - (😠 "Angry Face") - (🤬 "Face with Symbols on Mouth") - ) - for nom = (s-replace " " "_" name) - for desc = (s-collapse-whitespace (or description "")) - concat (concat - ;; f_… ⇒ get emoji from company menu showing only name & emoji - (format "\n** f_%s: %s %s \n%s" nom emoji "" emoji) - ;; fd_… ⇒ get emoji from company menu showing name, emoji, & ‘d’escription - (format "\n** fd_%s: %s %s \n%s" nom emoji desc emoji))) -#+end_src +(defun magit-commit-prompt () + "Magit prompt and insert commit header with faces." + (interactive) + (when use-magit-commit-prompt-p + (setq use-magit-commit-prompt-p nil) + (thread-last (--map (format "%s %s" (car it) (cdr it)) pretty-magit-prompt) + (completing-read "Insert commit leader ∷ ") + ;; My “Generate:” commit type has one use case, for now; so let's insert it filled-in. + (funcall (lambda (it) (if (s-starts-with? "Generate:" it) it (car (s-split " " it))))) + (insert) + (end-of-line)) + (add-magit-faces))) -#+begin_src emacs-lisp -;; Get all unicode emojis to appear within Emacs -;; See also: https://emacs.stackexchange.com/questions/5689/force-a-single-font-for-all-unicode-glyphs?rq=1 -(unless noninteractive (set-fontset-font t nil "Apple Color Emoji")) -#+end_src -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :noweb yes :exports none -<> +(remove-hook 'git-commit-setup-hook 'with-editor-usage-message) +(add-hook 'git-commit-setup-hook 'magit-commit-prompt) +(advice-add 'magit-commit :after 'use-magit-commit-prompt) #+end_src +#+end_details -***   =my_⋯= Templates to obtain User Information +** Version Control with SVN ---Using Magit! :Disabled: :PROPERTIES: -:CUSTOM_ID: my-Templates-to-obtain-User-Information +:CUSTOM_ID: Version-Control-with-SVN-Using-Magit +:header-args: :tangle no :END: -Let's add templates for links to common user information ^_^ - -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none -,** my_name: User's name -`user-full-name` +| /Disabled: I seldom work with SVN anymore./ | -,** my_email: User's email address -`user-mail-address` +Let's use git as an interface to subversion repositories so that we can continue +to use =magit= as our version control interface. The utility to do so is called +=git svn= ---note =git 𝒳= on a MacOS is the same as =git-𝒳= on other systems. -,** my_github: User's Github repoistory link -https://github.com/alhassy/ +#+begin_src emacs-lisp :tangle no +(use-package magit-svn + :hook (magit-mode . magit-svn-mode)) +#+end_src -,** my_emacsdrepo: User's version controlled Emacs init file -https://github.com/alhassy/emacs.d +Here's an example. The following command checksout an SVN repo; afterwhich we may open a file +there and do =M-x magit-status= to get the expected porcelain git interface ^_^ +#+begin_src emacs-lisp :tangle no +(async-shell-command "mkdir ~/2fa3; git svn clone --username alhassm https://websvn.mcmaster.ca/csse2fa3/2019-2020_Term2 ~/2fa3/") +#+end_src -,** my_blog: User's blog website -https://alhassy.github.io/ +In the magit buffer, we may now use the ~N~ key which wraps the =git svn= +subcommands =fetch, rebase, dcommit, branch, tag=. For example: -,** my_webpage: User's organisation website -http://www.cas.mcmaster.ca/~alhassm/ +1. Make changes to a file. +2. ‘Stage’ them with ~s~ and ‘commit’ them with ~c~. +3. ‘Push’ changes with ~N c~. -,** my_twitter: User's Twitter profile -https://twitter.com/musa314 +We get to pretend we're using ~git~ even though the underlying mechanism is ~svn~! +# Indeed, in a terminal ~git svn log~ will show an ~svn~ log with single pushed commits. -,** my_masters_thesis -A Mechanisation of Internal Galois Connections In Order Theory Formalised Without Meets -https://macsphere.mcmaster.ca/bitstream/11375/17276/2/thesis.pdf -#+end_src +For move on =git svn=, see [[https://gist.github.com/rickyah/7bc2de953ce42ba07116][A simple guide to git-svn]] or [[https://www.viget.com/articles/effectively-using-git-with-subversion/][Effectively using Git with + Subversion]]. -It may be useful to also have Org-link variants of these … +(If I need to work with svn repos often enough, I'd extend my ~maybe-clone~ + utility above to account for them.) -*** Templates from other places in my init +** Highlighting TODO-s & Showing them in Magit :PROPERTIES: -:CUSTOM_ID: Activate-templates-from-other-places-in-my-init +:CUSTOM_ID: Highlighting-TODO-s-Showing-them-in-Magit :END: -In this setup, I have some templates appear elsewhere, tagged with =:noweb-ref -templates-from-other-places-in-my-init=. They are presented in natural -positions, but can only occur to the machine after template expansion is setup. -Using org-mode, we are able to /present/ code in any order and /tangle/ it to -the order the compilers need it to be! - -Let's activate all such templates, now after template expansion has been setup. -#+begin_src org :tangle no -,#+begin_src org :noweb yes :tangle "~/.emacs.d/yankpad.org" :comments none -<> -,#+end_src -#+end_src - -You can press kbd:C-c_C-v_C-v, doc:org-babel-expand-src-block, to see what this -block expands into... -#+begin_details Expansion -# This is also live. -#+begin_src org :noweb yes :tangle "~/.emacs.d/yankpad.org" :comments none -<> -#+end_src -#+end_details +Sometimes it's nice to flag a chunk of text by its author, such as ‘ [[color:#dc8cc3][MA]] ’ for +‘M’usa ‘A’l-hassy, or ‘ [[color:#d0bf8f][HACK]] ’ for text that needs to be improved. Such flags +stand out from other text by being coloured and bold. -Note: Since I've insisted that Org blocks are space sensative, any whitespace -before the ~<<⋯>>~ will propogate to the resulting extracted code. +#+BEGIN_SRC emacs-lisp +;; NOTE that the highlighting works even in comments. +(use-package hl-todo + ;; I want todo-words highlighted in prose, not just in code fragements. + :hook (org-mode . hl-todo-mode) + :config + ;; Adding new keywords + (cl-loop for kw in '("TEST" "MA" "WK" "JC") + do (add-to-list 'hl-todo-keyword-faces (cons kw "#dc8cc3"))) + ;; Enable it everywhere. + (global-hl-todo-mode)) +#+END_SRC -#+begin_box Warning! :background-color red -This section had -#+begin_src org :tangle no -:PROPERTIES: -:CUSTOM_ID: Templates-from-other-places-in-my-init -:END: -#+end_src -Which, as of Org 9.4, led to the entire section being tangled: This is what the -above incantation requested, but I thought it only worked on src blocks, having -the specified ~:noweb-ref~, not on ~:CUSTOM_ID:~ incidentally having the same -name. -#+end_box +We've added few to the default flag keywords so that in total we have the +following flags ---where any sequence of at least 3 [[color:#cc9393][XXX]] are considered flags. +#+BEGIN_SRC emacs-lisp :results replace raw value :exports results :eval no-export :tangle no +(cl-loop for (k . f) in hl-todo-keyword-faces + collect (format "[[color:%s][%s]]" f k)) +#+END_SRC -** Prettify inline source code -:PROPERTIES: -:CUSTOM_ID: Prettify-inline-source-code -:END: +#+RESULTS: +([[color:#dc8cc3][JC]] [[color:#dc8cc3][WK]] [[color:#dc8cc3][MA]] [[color:#dc8cc3][TEST]] [[color:#d0bf8f][HOLD]] [[color:#cc9393][TODO]] [[color:#dca3a3][NEXT]] [[color:#dc8cc3][THEM]] [[color:#7cb8bb][PROG]] [[color:#7cb8bb][OKAY]] [[color:#5f7f5f][DONT]] [[color:#8c5353][FAIL]] [[color:#afd8af][DONE]] [[color:#d0bf8f][NOTE]] [[color:#d0bf8f][KLUDGE]] [[color:#d0bf8f][HACK]] [[color:#d0bf8f][TEMP]] [[color:#cc9393][FIXME]] [[color:#cc9393][XXX+]]) +:Alternate_approach_using_font_lock: #+begin_src emacs-lisp -;; Show “ src_emacs-lisp[:exports results]{ 𝒳 } ” as “ ℰ𝓁𝒾𝓈𝓅﴾ 𝒳 ﴿ ”. -;; -(font-lock-add-keywords 'org-mode - '(("\\(src_emacs-lisp\\[.*]{\\)\\([^}]*\\)\\(}\\)" - (1 '(face (:inherit (bold) :foreground "gray65") display "ℰ𝓁𝒾𝓈𝓅﴾")) - (2 '(face (:foreground "blue"))) - (3 '(face (:inherit (bold) :foreground "gray65") display "﴿")) - ))) -;; -;; Let's do this for all my languages: -;; Show “ src_LANGUAGE[…]{ ⋯ } ” as “ ﴾ ⋯ ﴿ ”. -(cl-loop for lang in my/programming-languages - do (font-lock-add-keywords 'org-mode - `(( ,(format "\\(src_%s\\[.*]{\\)\\([^}]*\\)\\(}\\)" lang) - (1 '(face (:inherit (bold) :foreground "gray65") display "﴾")) - (2 '(face (:foreground "blue"))) - (3 '(face (:inherit (bold) :foreground "gray65") display "﴿")) - )))) +(defun add-watchwords () "Add TODO: words to font-lock keywords." + (font-lock-add-keywords nil + '(("\\(\\ jump to first and final semantic units. +;; If pressed twice, they go to physical first and last positions. +(use-package beginend + :config (beginend-global-mode)) +#+END_SRC +* COMMENT Init.el misc +#+begin_src emacs-lisp ++ ++;; HACK: The system-packages-ensure pkg is neat and all, but I know I'm using brew. So I'll just define the utility myself, rather than loading an entire pkg. ++;; Moreover, my custom form has the added benefit of only installed packages that are not already installed, thereby foregoing any error messages which crash my init.el ++(defun system-packages-ensure (pkg) ++ (unless (s-contains-p pkg my/installed-packages) ++ (message "Installing ... %s" pkg) ++ ;; to call async shell command without a buffer you need to run: ++ ;; (shell-command "something&" (universal-argument)) ++ (ignore-errors (shell-command-to-string (format "brew install %s &" pkg) (universal-argument))))) -[[https://orgmode.org/org.html#Setting-up-capture][Capture]] lets me quickly make notes & capture ideas, with associated reference -material, without any interruption to the current work flow. Without losing -focus on what you're doing, quickly jot down a note of something important that -just came up. -#+begin_details ‘my/org-capture’ Implementation -#+begin_src emacs-lisp -(cl-defun my/org-capture-buffer (&optional keys no-additional-remarks - (heading-regexp "Subject: \\(.*\\)")) - "Capture the current [narrowed] buffer as a todo/note. +(load (shell-command-to-string "agda-mode locate")) -This is mostly intended for capturing mail as todo tasks ^_^ -When NO-ADDITIONAL-REMARKS is provided, and a heading is found, -then make and store the note without showing a pop-up. -This is useful for when we capture self-contained mail. -The HEADING-REGEXP must have a regexp parenthesis construction -which is used to obtain a suitable heading for the resulting todo/note." - (interactive "P") - (let* ((current-content (substring-no-properties (buffer-string))) - (heading (progn (string-match heading-regexp current-content) - (or (match-string 1 current-content) "")))) - (org-capture keys) - (insert heading "\n\n\n\n" (s-repeat 80 "-") "\n\n\n" current-content) ++;; Prevent undo tree files from polluting your git repo ++(setq undo-tree-history-directory-alist '(("." . "~/.emacs.d/undo"))) ++;; That will persist the undo history in ~/.emacs.d/undo instead of locally. ++ ++(set-face-font 'default "Source Code Pro Light 14") +#+end_src - ;; The overtly verbose conditions are for the sake of clarity. - ;; Moreover, even though the final could have “t”, being explicit - ;; communicates exactly the necessary conditions. - ;; Being so verbose leads to mutual exclusive clauses, whence order is irrelevant. - (cond - ((s-blank? heading) - (beginning-of-buffer) (end-of-line)) - ((and no-additional-remarks (not (s-blank? heading))) - (org-capture-finalize)) - ((not (or no-additional-remarks (s-blank? heading))) - (beginning-of-buffer) (forward-line 2) (indent-for-tab-command))))) -#+end_src -With that in-hand, we use a wrapper to ~org-capture~ to make use of it. #+begin_src emacs-lisp -(defun my/org-capture (&optional prefix keys) - "Capture something! ++;; Use “M-x set-face-font RET default RET”, or... ++;; (set-face-font 'default "Source Code Pro Light14") ++ ++;; See ~2232 fonts ++;; (append (fontset-list) (x-list-fonts "*" nil)) ++ ++ (cl-defun my/toggle-font (&optional (new-font (pop my/fonts))) ++ "Load NEW-FONT, which defaults from ‘my/fonts’. ++ ++When a universal prefix is given, “C-u C-c F”, we load a random ++font from all possible themes. Nice way to learn about more ++fonts (•̀ᴗ•́)و" ++ (interactive) ++ (let* ((all-fonts (append (fontset-list) (x-list-fonts "*" nil))) ++ (font (if current-prefix-arg ++ (nth (random (length all-fonts)) all-fonts) ++ new-font))) ++ (set-face-font 'default font) ++ (message "Font: %s" font))) ++ ++ (global-set-key "\C-c\ F" 'my/toggle-font) ++ ++ ;; Default font; the “ignore-⋯” is for users who may not have the font. ++ (ignore-errors (my/toggle-font "Fantasque Sans Mono 12")) ++ (ignore-errors (my/toggle-font "Source Code Pro Light 14")) ++ ++(unless noninteractive ++ (my/toggle-font "Roboto Mono Light 14") ++ (my/toggle-theme 'solarized-gruvbox-light)) ++ +(unless noninteractive + (load-file (let ((coding-system-for-read 'utf-8)) + (shell-command-to-string "/opt/homebrew/Cellar/agda/2.6.2.2_3/bin/agda-mode locate")))) ++ ++;; HACK: The system-packages-ensure pkg is neat and all, but I know I'm using brew. So I'll just define the utility myself, rather than loading an entire pkg. ++;; Moreover, my custom form has the added benefit of only installed packages that are not already installed, thereby foregoing any error messages which crash my init.el ++(defun system-packages-ensure (pkg) ++ (unless (s-contains-p pkg my/installed-packages) ++ (message "Installing ... %s" pkg) ++ ;; to call async shell command without a buffer you need to run: ++ ;; (shell-command "something&" (universal-argument)) ++ (ignore-errors (shell-command-to-string (format "brew install %s &" pkg) (universal-argument))))) +#+end_src - C-c c ⇒ Capture something -C-u C-c c ⇒ Capture current [narrowed] buffer. -C-u 5 C-c c ⇒ Capture current [narrowed] buffer without adding additional remarks. -C-u C-u C-c c ⇒ Goto last note stored. +* Cosmetics +:PROPERTIES: +:CUSTOM_ID: Cosmetics +:header-args: :tangle init.el +:END: -At work, ‘C-c c’ just captures notes under ‘Tasks’; no menu used." - (interactive "p") - (pcase prefix - (4 (my/org-capture-buffer keys)) - (5 (my/org-capture-buffer keys :no-additional-remarks)) - (t (if my/personal-machine? - (org-capture prefix keys) - (org-capture prefix "t"))))) -#+end_src -#+end_details +Upon startup, we want to be greeted with a useful, yet unobtrusive, message +briefly detailing major system details. Moreover, the bottom-most area of the +screen should display battery life, data, & time. Likewise, we may have a casual +file explorer ---primarily to show-off to newcomers, since great functionality +is found with ~M-x dired~ ---doc:dired. -#+begin_src emacs-lisp :results raw replace :exports results :wrap box -(s-join "\n" (--map (concat "+ [[kbd:" (s-replace "⇒" "]]" it)) (cddr (s-split "\n" (documentation #'my/org-capture))))) -#+end_src +#+BEGIN_SRC emacs-lisp :tangle init.el +;; Get org-headers to look pretty! E.g., * → ⊙, ** ↦ ◯, *** ↦ ★ +;; https://github.com/emacsorphanage/org-bullets +(use-package org-bullets + :defer nil + :hook (org-mode . org-bullets-mode)) +#+END_SRC -#+RESULTS: -#+begin_box -+ [[kbd: C-c c ]] Capture something -+ [[kbd:C-u C-c c ]] Capture current [narrowed] buffer. -+ [[kbd:C-u 5 C-c c ]] Capture current [narrowed] buffer without adding additional remarks. -+ [[kbd:C-u C-u C-c c ]] Goto last note stored. -#+end_box +** Startup message: Emacs & Org versions +:PROPERTIES: +:CUSTOM_ID: Startup-message-Emacs-Org-versions +:END: -E.g., I have a task, or something I wish to note down, rather than opening some -file, then making a heading, then writing it; instead, I press kbd:C-c_c_t and a -pop-up appears, I make my note, and it disappears ---with my notes file(s) now -being altered! Moreover, by default it provides a timestamp and a link to the -file location where I made the note ---helpful for tasks, tickets, to be tackled -later on. +Let's always welcome ourselves when Emacs begins with a helpful message. For +example, which user account is running and what are the version numbers of our +primary tools. #+begin_src emacs-lisp -;; Location of my todos / captured notes file -(unless noninteractive - (setq org-default-notes-file - (if my/personal-machine? - "~/Dropbox/todo.org" - "~/Desktop/Work-2022-01-01.org"))) +;; Silence the usual message: Get more info using the about page via C-h C-a. +(setq inhibit-startup-message t) -;; “C-c c” to quickly capture a task/note -(define-key global-map "\C-cc" #'my/org-capture) ;; See above. +(defun display-startup-echo-area-message () + "The message that is shown after ‘user-init-file’ is loaded." + (message + (concat "Welcome " user-full-name + "! Emacs " emacs-version + "; Org-mode " org-version + "; System " (symbol-name system-type) + "/" (system-name) + "; Time " (emacs-init-time)))) #+end_src +Now my startup message is, +#+begin_example +Welcome Musa Al-hassy! Emacs 27.1; Org-mode 9.4.4; System darwin/Musas-MacBook-Air.local; Time 13.331914 seconds +#+end_example +:Manually_Computing_Init_Time: +#+BEGIN_SRC emacs-lisp :tangle no +(format "; Time %.3fs" + (float-time (time-subtract (current-time) before-init-time))) +#+END_SRC +:End: -#+begin_details "By default we only get a ‘tasks’ form of capture, let's add some more." +Let's change the Emacs frame to mention the name of the buffer in focus, +as well as a nice ‘motto’: #+begin_src emacs-lisp -(cl-defun my/make-org-capture-template - (shortcut heading &optional (no-todo nil) (description heading) (scheduled nil)) - "Quickly produce an org-capture-template. +;; Keep self motivated! +(setq frame-title-format '("" "%b - Living The Dream (•̀ᴗ•́)و")) +#+end_src +** My to-do list: The initial buffer when Emacs opens up +:PROPERTIES: +:CUSTOM_ID: My-to-do-list-The-initial-buffer-when-Emacs-opens-up +:END: - After adding the result of this function to ‘org-capture-templates’, - we will be able perform a capture with “C-c c ‘shortcut’” - which will have description ‘description’. - It will be added to the tasks file under heading ‘heading’. +I almost always have Emacs open; I don't need a dashboard, but would like to see +my to-do list and my init file, side-by-side. +#+BEGIN_SRC emacs-lisp +;; If work machine, then show notes; otherwise show my todos & init side-by-side. +(unless noninteractive + ;; Only run the following when we're in GUI mode; + ;; i.e., don't run it in Github Actions when testing. + (if (not my/personal-machine?) + (find-file "/Users/musa/Google Drive/My Drive/notes.org")) ;; Org-journal for work + (find-file "~/Dropbox/todo.org") + ;; After startup, if Emacs is idle for 10 seconds, then open my work file; + ;; which is a GPG file and so requires passphrase before other things can load. + ;; (run-with-idle-timer 10 nil (lambda () (find-file "~/Desktop/work.org.gpg"))) + (split-window-right) ;; C-x 3 + (other-window 1) ;; C-x 0 + (let ((enable-local-variables :all) ;; Load *all* locals. + (org-confirm-babel-evaluate nil)) ;; Eval *all* blocks. + (ignore-errors (find-file "~/.emacs.d/init.org")))) - ‘no-todo’ omits the ‘TODO’ tag from the resulting item; e.g., - when it's merely an interesting note that needn't be acted upon. +#+END_SRC - Default for ‘description’ is ‘heading’. Default for ‘no-todo’ is ‘nil’. +There is the neat-looking [[https://github.com/emacs-dashboard/emacs-dashboard][emacs-dashboard]] package that provides an extensbile +yet minimalist splash screen showing recent files, projects, and bookmarks. - Scheduled items appear in the agenda; true by default. +** A sleek, informative, & fancy mode line +:PROPERTIES: +:CUSTOM_ID: A-sleek-informative-and-fancy-mode-line +:END: - The target is ‘file+headline’ and the type is ‘entry’; to see - other possibilities invoke: C-h o RET org-capture-templates. - The “%?” indicates the location of the Cursor, in the template, - when forming the entry. - " - `(,shortcut ,description entry - (file+headline org-default-notes-file ,heading) - ,(concat "*" (unless no-todo " TODO") " %?\n" - (when nil ;; this turned out to be a teribble idea. - ":PROPERTIES:\n:" - (if scheduled - "SCHEDULED: %^{Any time ≈ no time! Please schedule this task!}t" - "CREATED: %U") - "\n:END:") "\n\n ") - :empty-lines 1 :time-prompt t)) -#+end_src +The ‘modeline’ is a part near the bottom of Emacs that gives information about +the current buffer, such as its file-type/‘major-mode’ and enabled +extensions/‘minor-modes’. Let's use the [[https://github.com/seagle0128/doom-modeline][doom-modeline]], which is a /sleek & +minimalistic, yet fancy/ setup with the following notable perks: -#+begin_src emacs-lisp -(setq org-capture-templates - (cl-loop for (shortcut heading) - in (-partition 2 '("t" "Tasks, Getting Things Done" - "r" "Reference Material" - "m" "Email" - "e" "Emacs (•̀ᴗ•́)و" - "i" "Islam" - "b" "Blog" - "a" "Arbitrary Reading and Learning" - "l" "Programming Languages" - "p" "Personal Matters")) - collect (my/make-org-capture-template shortcut heading))) -#+end_src ++ Gives each buffer a nice icon in the modeline (denoting its major mode; e.g., + Lisp/JavaScript/Org/etc each get a cool icon). -Rather than adding notes to particular Org headings in my =todo.org= file, I could -defer such a choice by having only one template and have ~C-c a~ automatically use -it. Then I could ‘refile’ tasks to their appropriate parent headings with ~w~. -This allows us to seperate the concerns of capturing ideas from doing any form -of processing. Something to consider. ++ Name of file becomes red when unsaved/modified. -#+begin_src emacs-lisp -;; Update: Let's schedule tasks during the GTD processing phase. -;; -;; For now, let's automatically schedule items a week in advance. -;; TODO: FIXME: This overwrites any scheduling I may have performed. -;; (defun my/org-capture-schedule () -;; (org-schedule nil "+7d")) -;; -;; (add-hook 'org-capture-before-finalize-hook 'my/org-capture-schedule) -#+end_src -#+end_details ++ Nice version control icon, with branch name. -For now I capture everything into a single file. One would ideally keep -separate client, project, information in its own org file. -+ ⇒ Org capture actually lets us add /any/ type of entry, ‘programmable template’, - to /any/ type of file! ⇐ - * Look at doc:my/make-org-capture-template, above, to notice that capture - actually lets you add /any/ type of item to /any/ file. -+ ( For now, I'm only using it to add entries to my tasks lists. ) -+ Org-protocol is a way to create capture notes in org-mode from other applications. ++ Name of file is of the shape is shown as “project/file.ext”, when a project + is detected using ~projectile.el~. -Let's also ensure *[[orange:TODO]]*-s respect hierarchical structure. -#+BEGIN_SRC emacs-lisp -;; Cannot mark an item DONE if it has a TODO child. -;; Conversely, all children must be DONE in-order for a parent to be DONE. -(setq org-enforce-todo-dependencies t) -#+END_SRC ++ Flycheck error reporting is ugly by default, and one would consider using + flycheck-status-emojis to make things look better in a simple modeline, but + Doom-modeline gives a nice status indicators for Flycheck. -*Where am I currently capturing?* -+ During meetings, when a nifty idea pops into my mind, I quickly capture it. - - I've found taking my laptop to meetings makes me an active listener - and I get much more out of my meetings since I'm taking notes. -+ Through out the day, as I browse the web, read, and work; random ideas pop-up, and I capture them indiscriminately. -+ I envision that for a phone call, I would open up a capture to make note of what the call entailed so I can review it later. -+ Yet another place to capture content is from mail, such as for reference - material, or self-contained tasks. - # See [[#Capturing-Mail-as-Todo-Notes][above]] for this discussion. -+ Anywhere you simply want to make a note, for the current heading, just press - kbd:C-c_C-z. The notes are just your remarks along with a timestamp; they are - collected at the top of the tree, under the heading. ++ Shows “+2” when the text scale is two above usual. - #+begin_src emacs-lisp - ;; Ensure notes are stored at the top of a tree. - (setq org-reverse-note-order nil) - #+end_src +For fine-grained control on what/how things appear, there is +doc:doom-modeline-def-modeline and doc:doom-modeline-set-modeline. -Quickly look up some reference material... -#+begin_src emacs-lisp -(cl-defun my/reference (&optional (file org-default-notes-file)) - "Look up some reference material super quick. +NOTE: We need to run =M-x= doc:nerd-icons-install-fonts for doom-modeline to use pretty icons ---then restart Emacs. +#+begin_src emacs-lisp :tangle init.el +;; The modeline looks really nice with doom-themes, e.g., doom-solarised-light. +(use-package doom-modeline + :defer nil + :config (doom-modeline-mode)) -By default we look in user's Org TODOs file. + ;; Use minimal height so icons still fit; modeline gets slightly larger when + ;; buffer is modified since the "save icon" shows up. Let's disable the icon. + ;; Let's also essentially disable the hud bar, a sort of progress-bar on where we are in the buffer. + (setq doom-modeline-height 1) + (setq doom-modeline-buffer-state-icon nil) + (setq doom-modeline-hud t) + (setq doom-modeline-bar-width 1) -FILE should be an ORG file with a top-level heading that starts with ‘Reference’. -We show its subheadings in a completing-read menu, then narrow to that entry." - (interactive) - (find-file file) - (widen) - (goto-char (point-min)) - (re-search-forward "^* Reference") ;; Start of line. - (org-narrow-to-subtree) - (org-cycle) (org-cycle) - (let* ((headings (org-map-entries (lambda () (org-element-property :title (org-element-at-point)) ) "LEVEL=2")) - (topic (completing-read "What to review? " headings))) - (search-forward (concat "** " topic)) - (org-narrow-to-subtree) - (org-cycle))) + ;; Show 3 Flycheck numbers: “red-error / yellow-warning / green-info”, which + ;; we can click to see a listing. + ;; If not for doom-modeline, we'd need to use flycheck-status-emoji.el. + (setq doom-modeline-checker-simple-format nil) -(defalias 'my/review-reference-notes 'my/reference) -(defalias 'w-reference 'my/reference) ;; “w”ork -#+end_src + ;; Don't display the buffer encoding, E.g., “UTF-8”. + (setq doom-modeline-buffer-encoding nil) -**** COMMENT Emacs as a reference platform: Instead of manually finding files, looking for :Place_beside_w_reference_function: -:PROPERTIES: -:CUSTOM_ID: COMMENT-Emacs-as-a-reference-platform-Instead-of-manually-finding-files-looking-for -:END: - info, then closing them; we can press a key combination to see some reference - matter _quickly_ then press _q_ to dismiss it just as quickly. We can do this with - a combination of (local-set-key (kbd "q") 'kill-buffer-and-window) and - (read-only-mode); but better would be to open a file and put it in special-mode - which has this _q_ behaviour [see simple.el special-mode-map for other bindinfs] - or to use the built-in with-electric-help macro, or make-help-screen & (require - 'help-macro).. Moreover, for lots of reference matter, a hydra can be used to - obtain a nice menu. My setup is to use doc:w-reference. + ;; Inactive buffers' modeline is greyed out. + ;; (let ((it "Source Code Pro Light" )) + ;; (set-face-attribute 'mode-line nil :family it :height 100) + ;; (set-face-attribute 'mode-line-inactive nil :family it :height 100)) + ;; A quick hacky way to add stuff to doom-modeline is to add to the mode-line-process list. + ;; E.g.: (add-to-list 'mode-line-process '(:eval (format "%s" (count-words (point-min) (point-max))))) + ;; We likely want to add this locally, to hooks on major modes. +#+end_src -Anyhow… -*** Step 1: When new tasks come up -:PROPERTIES: -:CUSTOM_ID: Step-1-When-new-tasks-come-up -:END: +:Disabled__spaceline_modeline: -Isn't it great that we can squirrel away info into some default location -then immediately return to what we were doing before ---with speed & minimal distraction! ♥‿♥ -Indeed, if our system for task management were slow then we may not produce -tasks and so forget them altogether! ~щ(゜ロ゜щ)~ +[[https://github.com/TheBB/spaceline][Spaceline]] -+ Entering tasks is a desirably impulsive act; do not make any further - scheduling considerations. +# I may not use the spacemacs [[https://www.emacswiki.org/emacs/StarterKits][starter kit]], since I find spacemacs to “hide things” +# from me ---whereas Emacs “encourages” me to learn more---, however it is a +# configuration and I enjoy reading Emacs configs in order to improve my own +# setup. From Spacemacs I've adopted Helm for list completion, its sleek light & +# dark themes, and its modified powerline setup. - The next step, the review stage occurring at the end or the start of the - workday, is for processing. +#+begin_src emacs-lisp :tangle no +;; When using helm & info & default, mode line looks prettier. +(use-package spaceline + :custom (spaceline-buffer-encoding-abbrev-p nil) + ;; Use an arrow to seperate modeline information + (powerline-default-separator 'arrow) + ;; Show “line-number : column-number” in modeline. + (spaceline-line-column-p t) + ;; Use two colours to indicate whether a buffer is modified or not. + (spaceline-highlight-face-func 'spaceline-highlight-face-modified) + :config (custom-set-faces '(spaceline-unmodified ((t (:foreground "black" :background "gold"))))) + (custom-set-faces '(spaceline-modified ((t (:foreground "black" :background "cyan"))))) + (require 'spaceline-config) + (spaceline-helm-mode) + (spaceline-info-mode) + (spaceline-emacs-theme)) -#+begin_quote -The reason for this is that entering new tasks should be impulsive, not reasoned. -Your reasoning skills are required for the task at hand, not every new tidbit. -You may even find that during the few hours that transpire between creating a -task and categorizing it, you’ve either already done it or discovered it doesn’t -need to be done at all! ---[[http://newartisans.com/2007/08/using-org-mode-as-a-day-planner/][John Wiegley]] -#+end_quote +(spaceline-toggle-buffer-size-off) ;; Not interested in how large a buffer is. +(spaceline-toggle-input-method-off) ;; Usually a “Π” symbol in the modeline, since I use Unicode input with Agda, denoting the current language environment, buffer coding system, and current input method. +(spaceline-toggle-buffer-encoding-abbrev-off) ;; buffer-encoding-abbrev: the line ending convention used in the current buffer (unix, dos or mac). +(spaceline-toggle-hud-off) ;; A tiny “progress bar”; shows the currently visible part of the buffer. +(spaceline-toggle-helm-buffer-id-off) ;; I don't need to see “Helm M-x” whenever I press “M-x” or other Helm-powered commands. +#+end_src -When my computer isn't handy, I'll make a note on my phone then transfer it later. -*** Step 2: Filing your tasks +Other separators ---of modeline information--- that I've considered include +~'brace~ instead of an arrow, and ~'contour, 'chamfer, 'wave, 'zigzag~ which look +like browser tabs that are curved, boxed, wavy, or in the style of driftwood. +:End: + +*** Menu to Toggle Minor Modes: A quick way to see all of my modes, and which are enabled :PROPERTIES: -:CUSTOM_ID: Step-2-Filing-your-tasks +:CUSTOM_ID: Menu-to-Toggle-Minor-Modes-A-quick-way-to-see-all-of-my-modes-and-which-are-enabled :END: -At a later time, a time of reflection, we go to our tasks list and actually -schedule time to get them done by kbd:C-c_C-s, doc:org-schedule, then pick a -date by entering a number in the form ~+𝓃~ to mean that task is due ~𝓃~ days from -now. - -+ Tasks with no due date are ones that “could happen anytime”, most likely no time at all. -+ At least schedule tasks reasonably far off in the future, then reassess when the time comes. -+ An uncompleted task is by default rescheduled to the current day, each day, along with how overdue it is. - - Aim to consciously reschedule such tasks! - - Let's keep track of how many times, and when, we have pushed events to other dates. - #+BEGIN_SRC emacs-lisp -;; Add a note whenever a task's deadline or scheduled date is changed. -(setq org-log-redeadline 'time) -(setq org-log-reschedule 'time) -#+END_SRC - -#+begin_box custard - With time, it will become clear what is an unreasonable day - verses what is an achievable day. -#+end_box -[[https://orgmode.org/manual/Repeated-tasks.html][Repeat tasks]] by a repeater such as ‘+1m’ or ‘+7d’ in their timestamps; e.g., -~:SCHEDULED: <2005-10-01 Sat +1m>.~ Likewise, to schedule an event (to repeat) for -multiple days, just use a bunch of timestamps (with repeaters): ~:SCHEDULED: -<2022-01-04 Tues 9:00 +1w><2022-01-06 Thu +1w><2022-01-07 Fri 9:00 +1w>~. -(Notice that we have ~:~ on each side of the keyword; and we have a time of day -for the event.) +Enabled minor modes clutter up the modeline with their names, albeit some have +useful status information shown. We can either selectively pick which +names/status are shown using diminish.el, possibly forgetting which minor modes +are enabled or we can use minions.el to “gather up” all enabled minor modes, and +recently enabled ones, under a single menu which doom-modeline shows as a simple +configurations gear icon. ⚙. :gear: --------------------------------------------------------------------------------- +#+begin_src emacs-lisp + (setq doom-modeline-minor-modes t) + (use-package minions -A ‘project’ is a task that has multiple steps, each as a checkbox item. It can -be given a percentage marker to show progress: Place ~[%]~ after its name, then -press [[kbd:C-c_#]] ---doc:org-update-statistics-cookies--- on the name to see a -completion percentage ---press kbd:C-c_C-c on a checkbox item to toggle its -completion state. + :init (minions-mode 1)) +#+end_src -*** Step 3: Quickly review the upcoming week +*** Nice battery icon alongside with percentage, in doom-modeline :PROPERTIES: -:CUSTOM_ID: Step-3-Quickly-review-the-upcoming-week +:CUSTOM_ID: Nice-battery-icon-alongside-with-percentage-in-doom-modeline :END: - -The next day we begin our work, we press kbd:C-c_a_a to see the scheduled tasks -for this week ---kbd:C-c_C-s to re-schedule the task under the cursor and [[kbd:r]] -to refresh the agenda. -#+begin_src emacs-lisp -(define-key global-map "\C-ca" 'org-agenda) +#+begin_src emacs-lisp :tangle init.el +;; If not for doom-modeline, we'd need to use fancy-battery-mode.el. +(display-battery-mode +1) #+end_src -+ Show the next 𝓃 days schedule ⇐ =C-u 𝓃 C-c a a=. - -# The next section, [[Super Agenda]], will discuss acting on entries in the agenda -# buffer. - --------------------------------------------------------------------------------- +#+begin_details [Posterity / Disabled] Fancy Battery Setup +Let's have it also show remaining battery life, coloured green if charging and +coloured yellow otherwise. It is important to note that this package is no +longer maintained. It works on my machine. +#+BEGIN_SRC emacs-lisp :tangle no +;; Let's use a fancy indicator … +(use-package fancy-battery + :custom (fancy-battery-show-percentage t) + (battery-update-interval 15) + :config (fancy-battery-mode)) +#+END_SRC +#+end_details -Org agenda is an interactive tool for generating summary reports from Org data ----e.g., commonly, the weekly task list is generated from todo tasks. +*** Time & date +:PROPERTIES: +:CUSTOM_ID: Time-date +:END: +Let’s display the current time, with updates every second. -The agenda dispatch menu, ~C-c a~, has options for displaying tasks ---e.g., ~C-c a -m~ generates a list of entries having the same tags. new ways to view tasks by -altering the ~org-agenda-custom-commands~ variable ---e.g., above we added two, -one for completed tasks and one for unscheduled tasks. +#+begin_src emacs-lisp :tangle init.el +;; Show date and time as well. -# Moreover, we can overlay due dates and priorities to tasks in a non-intrusive -# way that is easy to edit by hand. -#+begin_details "Let's setup the basics of our agenda." -#+begin_src emacs-lisp -;; List of all the files & directories where todo items can be found. Only one -;; for now: My default notes file. -(setq org-agenda-files (list org-default-notes-file)) +;; [Simple Approach] +;; (setq display-time-day-and-date t) +;; (display-time) -;; Display tags really close to their tasks. -(setq org-agenda-tags-column -10) +;; [More Controlled Approach: Set date&time format] +;; a ≈ weekday; b ≈ month; d ≈ numeric day, R ≈ 24hr:minute. +(setq display-time-format "%a %b %d ╱ %r") ;; E.g.,: Fri Mar 04 ╱ 03:42:08 pm +(setq display-time-interval 1) ;; Please update the time every second. +(display-time-mode) +#+end_src -;; How many days ahead the default agenda view should look -(setq org-agenda-span 'day) -;; May be any number; the larger the slower it takes to generate the view. -;; One day is thus the fastest ^_^ +But doc:display-time-mode shows me a bit more info that I actually don't care +for; so let's disable those. +#+begin_src emacs-lisp :tangle init.el +;; I don't need the system load average in the modeline. +(setq display-time-default-load-average nil) +(setq display-time-load-average nil) +#+end_src -;; How many days early a deadline item will begin showing up in your agenda list. -(setq org-deadline-warning-days 14) +*** No Line, nor Column, Numbers ---and no buffer percentage +Likewise, let's have the modeline display column numbers, but not line numbers. +Instead, let's have line numbers on the side of the buffer; moreover let's have +a uniform width for displaying line numbers, rather than having the width grow +as necessary. +#+BEGIN_SRC emacs-lisp :tangle init.el +;; ;; Do not show me line numbers, nor column numbers, in the modeline +(column-number-mode -1) +(line-number-mode -1) -;; In the agenda view, days that have no associated tasks will still have a line showing the date. -(setq org-agenda-show-all-dates t) +;; Likewise, no need to show me “Top∣Mid∣Bot” in the modeline. +(setq mode-line-percent-position nil) +#+END_SRC -;; Scheduled items marked as complete will not show up in your agenda view. -(setq org-agenda-skip-scheduled-if-done t) -(setq org-agenda-skip-deadline-if-done t) -#+end_src -#+end_details +Line numbers are a /conventionally expected/ part of a user interface, but I've +realised that I seldom /need to see/ them. I can still jump to a line number +provided by a compilation error with kbd:M-g_g; and toggle line numbers on when +I'm pair programming with doc:display-line-numbers-mode. +- In Emacs, there are /buffer/ which exist and contain textual data, but to + actually see them one requires a /window/. In the same vein, there are line + numbers but I don't need to always see them. +- If I need an indication of ‘progress’, the modeline contains a percentage of + how far I am in a buffer. +#+BEGIN_SRC emacs-lisp :tangle init.el +;; (setq display-line-numbers-width-start t) +;; (global-display-line-numbers-mode t) +#+END_SRC +*** Buffer names are necessarily injective :No_longer_needed_due_to_doom:Disabled: +:PROPERTIES: +:CUSTOM_ID: Buffer-names-are-necessarily-injective +:header-args: :tangle no +:END: -#+begin_details Super Simple ‘agenda’ Mini-tutorial -link-here:Super-Simple-‘agenda’-Mini-tutorial +By default when multiple files sharing the same name are opened, say for +comparison from different directories, their buffers are named uniquely by +having the format “⟨file-name⟩ <𝓃>”, for numbers 𝓃. It'd be more helpful +to have the buffer names reflect their location. +#+BEGIN_SRC emacs-lisp +;; Note that ‘uniquify’ is builtin. +(require 'uniquify) +(setq uniquify-separator "/" ;; The separator in buffer names. + uniquify-buffer-name-style 'forward) ;; names/in/this/style +#+END_SRC -The agenda view, like nearly all Emacs entities, is interactive: -+ =𝓃 f,b= ⇒ Look forward at next week's agenda, or backward to a previous week. - - The optional $𝓃$ means do the action =𝓃=-many times; - it defaults to 1. -+ =w, d= ⇒ toggle week view, or day view; use ~v~ to see possible views. - - E.g., ~C-u 2017 v y~ shows us the specific year 2017. -+ =𝓃 n,p= to navigate to next and previous entries. -+ =t= ⇒ cycle TODO state of the current entry. -+ =±= ⇒ cycle priority state. -+ =𝓃 S-⇆= ⇒ Shift date time by $𝓃$ days; 1 day by default. -+ =C-c C-s= ⇒ Reschedule an entry; prefix it with ~C-u~ to remove a scheduled entry. - - [[https://orgmode.org/manual/Repeated-tasks.html][Repeat tasks]] by a repeater such as ‘+1m’ or ‘+7d’ - in their timestamps; e.g., ~DEADLINE: <2005-10-01 Sat +1m>.~ -+ =s= ⇒ save all agenda buffers; i.e., save the org-files where the agenda items live. -+ =g= ⇒ Rebuild agenda according to any changes made thus far. -+ =F= ⇒ Toggle ‘follow mode’: As you go up/down entries, you can see their - details in an adjacent window. - - =SPC= ⇒ Show details of a single entry in other window; stay in Agenda. -+ =RET, TAB= ⇒ Go to the current entry in the current window or in a new - adjacent window, so as to alter task details. + Note that this does not affect cloning buffers, kbd:C-x_4_c. -# ;; Easy way to capture tasks when in agenda view. -# (define-key org-agenda-mode-map "c" #'org-capture) +( A function /f/ is /injective/ precisely when it's /distinction-preserving/; i.e., + /x ≠ y ≡ f x ≠ f y/. We can tell whether two things are the same or not, by + ‘zooming in’ on their particular property ‘f’, which may be easier to compare. + E.g., object IDs, hashcodes, unique keys in database tables. ) -#+end_details +( Why am I bringing this up? I like math and seldom get to use it; so why not! ) -The agenda view –--even in the 7-days-at-a-time view--– will always begin on the -current day. This is important, since while using org-mode as a day planner, -you never want to think of days gone past. That’s something you do in other -ways, such as when reviewing completed tasks. -#+begin_src emacs-lisp -(setq org-agenda-start-on-weekday nil) +** Exquisite Fonts and Themes +:PROPERTIES: +:CUSTOM_ID: Exquisite-Fonts-and-Themes +:END: -;; Start each agenda item with ‘○’, then show me it's %timestamp and how many -;; times it's been re-%scheduled. -(setq org-agenda-prefix-format " ○ %t%s") -#+end_src +Emacs' default theme leaves much to be desired: It does not look sleek and +shiny, which usually leaves first-timers with a poor, shallow, impression of the +system. Below we install a few themes that make Emacs look exquisite. We cycle +between the chosen themes with kbd:C-c_t, doc:my/toggle-theme. -#+begin_details Grouping agenda entries together -Instead of having the day's tasks all in one field, org-super-agenda allows us -to use predicates to group entries together; e.g., by considering an entry's -=:tags:= or its priority level. Since I'm placing all my tasks in a single file, -under appropriate parent headings, I want entries to be shown according to their -parent heading. Of-course, the top-most grouping, the important tasks, should be -pulled out of their group and placed at the top. -#+BEGIN_SRC emacs-lisp -(use-package origami) -(use-package org-super-agenda - :hook (org-agenda-mode . origami-mode) ;; Easily fold groups via TAB. - :bind (:map org-super-agenda-header-map ("" . origami-toggle-node)) - :config - (org-super-agenda-mode) - (setq org-super-agenda-groups - '((:name "Important" :priority "A") - (:name "Personal" :habit t) - ;; For everything else, nicely display their heading hierarchy list. - (:auto-map (lambda (e) (org-format-outline-path (org-get-outline-path))))))) +#+begin_details “my/toggle-theme” Implementation ++ ~M-x load-theme RET TAB~ shows all themes, including built-in ones, + that may be loaded. ++ Loading multiple themes results in their pallets mixed. + - ~M-x disable-theme~ to remove a theme from the current pallet. -;; MA: No noticable effect when using org-super-agenda :/ -;; -;; Leave new line at the end of an entry. -;; (setq org-blank-before-new-entry '((heading . t) (plain-list-item . t))) -#+END_SRC +-------------------------------------------------------------------------------- -:OlderSetup: -#+BEGIN_SRC emacs-lisp :tangle no - ;; Default order is 0, first come first serve. - ;; Items are “or”-ed by default. - '((:name "Important" - :tag "PackageFormer" - :tag "PF" - :tag "research" - :and (:tag "JC" :priority "A") - :and (:tag "WK" :priority "A") - :priority "A") +#+begin_src emacs-lisp :tangle init.el +;; Treat all themes as safe; no query before use. +(setf custom-safe-themes t) - ;; Groups supply their own section names when none are given - (:name "Living the dream!" :tag "personal") - (:name "Discrete Mathematics" :tag "2fa3") - (:name "Emacs Init" :tag "init") - ;; (:priority<= "B" :order 1) - ;; Everything else automatically gets its own group - (:auto-category t) - ))) -#+END_SRC -:End: -# + Origami mode ---see below in [[Text Folding with Origami-mode]]--- -# works well with super-agenda. Just ~M-x origami-mode~ then ~C-c f~ to -# enable the folding hydra. -# -The [[https://github.com/alphapapa/org-super-agenda][org-super-agenda]] homepage shows complex configurations and pleasant -screenshots contrasting with and without the system. -E.g., you can change how entries in particular headings are displayed and coloured. -#+end_details -*** Step 4: Getting ready for the day -:PROPERTIES: -:CUSTOM_ID: Step-4-Getting-ready-for-the-day -:END: -After having seen our tasks for the week, we press [[kbd:d]] to enter daily view for -the current day. Now we decide whether the items for today are ~A~: of high -urgency & important; ~B~: of moderate urgency & importance; or ~C~: Pretty much -optional, or very quick or fun to do. -+ ~A~ tasks should be both important /and/ urgently done on the day they were scheduled. - - Such tasks should be relatively rare! - - If you have too many, you're anxious about priorities and rendering - priorities useless. -+ ~C~ tasks can always be scheduled for another day without much worry. - - Act! If the thought of rescheduling causes you to worry, upgrade it to a - ~B~ or ~A~. -+ As such, most tasks will generally be priority ~B~: - Tasks that need to be done, but the exact day isn't as critical as with an - ~A~ task. These are the “bread and butter” tasks that make up your day to day - life. +;; Nice looking themes ^_^ +(use-package solarized-theme ) +(use-package doom-themes ) +(use-package spacemacs-common -On a task item, or any org-heading, press [[kbd:,]] then one of [[kbd:A/B/C]] to set its -priority. Then [[kbd:r]] to refresh. + :ensure spacemacs-theme) +(use-package stimmung-themes ) +(use-package shanty-themes ) -#+begin_details Pretty Prioritisation Markers -link-here:Pretty-Prioritisation-Markers -Let's set four priority levels and their colours: The more intense colours -are for more urgent tasks. -#+begin_src emacs-lisp -(setq org-lowest-priority ?C) ;; Now org-speed-eky ‘,’ gives 3 options -(setq org-priority-faces -'((?A :foreground "red" :weight bold) ;; :background "LightCyan1") - (?B :foreground "orange" :weight bold) - (?C :foreground "green" :weight bold))) -;; See all colours with: M-x list-colors-display #+end_src -+ ~C-c ,~ anywhere to set the priority of the current heading. - - We may press ~A-D~ or ~SPC~ to an remove existing priority. - -Priority markers are of the form ~[#𝒳]~, the [[https://github.com/harrybournis/org-fancy-priorities][fancy priorities]] package visually -renders them as words or icons. -#+begin_src emacs-lisp -(use-package org-fancy-priorities - :hook (org-mode . org-fancy-priorities-mode) - :custom (org-fancy-priorities-list '("High" "MID" "LOW")) ;; "OPTIONAL" - ;; Let's use the “Eisenhower map of priority”… - ;; :custom (org-fancy-priorities-list '("Urgent and Important" ;; Do now! - ;; "Not Urgent But Important" ;; Do schedule this. - ;; "Urgent But Not Important" ;; Delegate? - ;; "Not Urgent and Not Important")) ;; Don't do / Optional - ) -#+end_src -#+end_details ++ The [[https://github.com/hlissner/emacs-doom-themes/tree/screenshots][Doom Themes]] also look rather appealing. ++ A showcase of many themes can be found [[https://emacsthemes.com/][here]]. -At work, my tasks list is massive and constantly growing; so to avoid stressing -myself out, I try to add /time effort estimates/ to my tasks with -doc:org-set-effort then I open my Org agenda and run ~d~ (for day view) then -doc:org-agenda-columns (~C-c C-x C-c~) to see a column-view of my time estimates -/along/ with *a total time estimate for the day.* Since there are unexpected -pair-programming calls, or meetings go longer than expected, I only schedule for -7 hours each day (in a usual 8-hour work day); i.e., if my time estimates exceed -7h then I reschedule or cancel some things. +-------------------------------------------------------------------------------- -#+begin_src emacs-lisp -(require 'org-agenda) +#+BEGIN_SRC emacs-lisp :tangle init.el +;; Infinite list of my commonly used themes. +(setq my/themes '(doom-laserwave shanty-themes-light stimmung-themes-light stimmung-themes-dark doom-solarized-light doom-vibrant spacemacs-light solarized-gruvbox-dark solarized-gruvbox-light)) +(setcdr (last my/themes) my/themes) +#+END_SRC -;; How should the columns view look? -(setq org-columns-default-format "%60ITEM(Task) %6Effort(Estim){:} %3PRIORITY %TAGS") +kbd:C-c_t to toggle between the personal themes. +#+BEGIN_SRC emacs-lisp :tangle init.el +(cl-defun my/load-theme (&optional (new-theme (completing-read "Theme: " (custom-available-themes)))) + "Disable all themes and load the given one ---read from user when called interactively." + (interactive) + (mapc #'disable-theme custom-enabled-themes) + (load-theme new-theme) + (message "Theme %s" new-theme)) -;; Press “c” in Org agenda to see the columns view; (default binding C-c C-x C-c is too long!) -(org-defkey org-agenda-mode-map "c" #'org-agenda-columns) -(org-defkey org-agenda-mode-map "C" #'org-agenda-goto-calendar) +(cl-defun my/toggle-theme (&optional (new-theme (pop my/themes))) + "Disable all themes and load NEW-THEME, which defaults from ‘my/themes’. -;; Press “e” in columns view to alter “e”ffort “e”stimates. -(require 'org-colview) -(org-defkey org-columns-map "e" - ;; Refresh after making an effort estimate. - (lambda () (interactive) (org-agenda-set-effort) (org-agenda-columns))) -#+end_src +When a universal prefix is given, “C-u C-c t”, we load a random +theme from all possible themes. Nice way to learn about more +themes (•̀ᴗ•́)و" + (interactive) + (-let [theme (if current-prefix-arg + (nth (random (length (custom-available-themes))) + (custom-available-themes)) + new-theme)] + (my/load-theme theme))) -Near the top of my TODOS file, I have the following clickable link: It looks pretty and when I click it, takes me where I need to go. -# ;; Show sum of efforts for a day in Org-Agenda day title -#+begin_src org :tangle no -[[elisp:(progn (org-agenda nil "a") (org-agenda-columns) (delete-other-windows))][Show agenda with time estimates]] -#+end_src +(global-set-key "\C-c\ t" 'my/toggle-theme) -*** Step 5: Doing the work -:PROPERTIES: -:CUSTOM_ID: Step-5-Doing-the-work -:END: +;; (my/toggle-theme) +(my/toggle-theme 'doom-laserwave) +#+END_SRC -Since ~A~ tasks are the important and urgent ones, if you do all of the ~A~ tasks and -nothing else today, no one would suffer. It's a good day (─‿‿─). +Apparently, there's already a package that accomplishes these goals and more: +[[https://github.com/myTerminal/theme-looper][theme-looper]]. I may switch to it, but for now my simple function above is +slightly informative, to me at least, about how themes work and it does what I +want. -There should be no scheduling nor prioritising at this stage. -You should not be touching your tasks file until your next review session: -Either at the end of the day or the start of the next. +...Actually, the above learning adventure has made it easy to provide a similar setup +for fonts 😁 +#+end_details -+ Leverage priorities! E.g., When a full day has several ~C~ tasks, reschedule - them for later in the week without a second thought. - - You've already provided consideration when assigning priorities. +Likewise, kbd:C-c_F, doc:my/toggle-font, to quickly change fonts (according to +mood 😸). [I already use kbd:C-c_f, doc:my/org-mode-format, for the more likely +operation of formatting text.] +#+begin_details “my/toggle-font” Implementation -*** Step 6: Moving a task toward completion -:PROPERTIES: -:CUSTOM_ID: Step-6-Moving-a-task-toward-completion -:END: +#+begin_src emacs-lisp :tangle init.el +(unless nil ;; my/work-machine? -My workflow states are described in the section -[[Workflow States]] and contain states: ~TODO, STARTED, WAITING, ON_HOLD, CANCELLED, DONE~. -+ Tasks marked ~WAITING~ are ones for which we are awaiting some event, like someone - to reply to our query. As such, these tasks can be rescheduled until I give up - or the awaited event happens ---in which case I go to ~STARTED~ and document - the reply to my query. -+ The task may be put off indefinitely with ~ON_HOLD~, or I may choose never to do it - with ~CANCELLED~. Along with ~DONE~, these three mark a task as completed - and so it needn't appear in any agenda view. + ;; Infinite list of my commonly used fonts + (setq my/fonts + '(;; NOPE: Breaks Gerrit! "Roboto Mono Light 14" ;; Sleek + "Input Mono 14" + "Source Code Pro Light 14" ;; thin, similar to Inconsolata Light + "Papyrus 14" + "Bradley Hand Light 12" + ;; "Chalkduster 14" ;; Laggy! + "Courier Light 12" + "Noteworthy 9" + "Savoye LET 14" + "Fantasque Sans Mono 16" + )) + (setcdr (last my/fonts) my/fonts) -I personally clock-in and clock-out of tasks ---keep reading---, where upon -clocking-out I'm prompted for a note about what I've accomplished so far. -Entering a comment about what I've done, even if it's very little, feels like -I'm getting something done. It's an explicit marker of progress. + ;; Let's ensure they're on our system + ;; brew search "/font-/" # List all fonts -In the past, I would make a “captain's log” at the end of the day, but that's -like commenting code after it's written, I didn't always feel like doing it and -it wasn't that important after the fact. The continuous approach of noting after -every clock-out is much more practical, for me at least. + (shell-command "brew tap homebrew/cask-fonts") + (system-packages-ensure "svn") ;; Required for the following font installs + ;; No thanks! (system-packages-ensure "font-roboto-mono") ;; Makes Gerrit in Chrome look like Gibberish! + (system-packages-ensure "font-input") + (system-packages-ensure "font-source-code-pro") + (system-packages-ensure "font-fira-mono") + (system-packages-ensure "font-mononoki") + (system-packages-ensure "font-monoid") + (system-packages-ensure "font-menlo-for-powerline") + (system-packages-ensure "font-fantasque-sans-mono") -*** Step 7: Archiving Tasks -:PROPERTIES: -:CUSTOM_ID: Step-7-Archiving-Tasks -:END: -During the review state, when a task is completed, ‘archive’ it with -doc:org-archive-subtree or kbd:C-c_C-x_C-s: This marks it as done, adds a time -stamp, and moves it to a local ~*.org_archive~ file. What was our *[[red:‘to do’]]* list -becomes a *[[green:‘ta da’]]* list showcasing all we have done (•̀ᴗ•́)و +;; Use “M-x set-face-font RET default RET”, or... +;; (set-face-font 'default "Source Code Pro Light14") -Archiving keeps task lists clutter free, but unlike deletion it allows us, -possibly rarely, to look up details of a task or what tasks were completed in a -certain time frame ---which may be a motivational act, to see that you have -actually completed more than you thought, provided you make and archive tasks -regularly. We can use [[doc:org-search-view][~M-x org-search-view~]] to search an org file /and/ the -archive file too, if we enable it so. -#+BEGIN_SRC emacs-lisp -;; C-c a s ➩ Search feature also looks into archived files. -;; Helpful when need to dig stuff up from the past. -(setq org-agenda-text-search-extra-files '(agenda-archives)) -#+END_SRC +;; See ~2232 fonts +;; (append (fontset-list) (x-list-fonts "*" nil)) -#+begin_src emacs-lisp -;; enables the org-agenda variables. -(require 'org-agenda) ;; Need this to have “org-agenda-custom-commands” defined. + (cl-defun my/toggle-font (&optional (new-font (pop my/fonts))) + "Load NEW-FONT, which defaults from ‘my/fonts’. -(unless noninteractive - ;; ➩ Show my agenda upon Emacs startup. - (org-agenda "a" "a")) -#+end_src +When a universal prefix is given, “C-u C-c F”, we load a random +font from all possible themes. Nice way to learn about more +fonts (•̀ᴗ•́)و" + (interactive) + (let* ((all-fonts (append (fontset-list) (x-list-fonts "*" nil))) + (font (if current-prefix-arg + (nth (random (length all-fonts)) all-fonts) + new-font))) + (set-face-font 'default font) + (message "Font: %s" font))) -Let's install some helpful views for our agenda. + (global-set-key "\C-c\ F" 'my/toggle-font) -+ ~C-c a c~: See completed tasks at the end of the day and archive them. - #+begin_src emacs-lisp -;; Pressing ‘c’ in the org-agenda view shows all completed tasks, -;; which should be archived. -(add-to-list 'org-agenda-custom-commands - '("c" todo "DONE|ON_HOLD|CANCELLED" nil)) + ;; Default font; the “ignore-⋯” is for users who may not have the font. + (ignore-errors (my/toggle-font "Fantasque Sans Mono 12")) + (ignore-errors (my/toggle-font "Source Code Pro Light 14"))) #+end_src -+ ~C-c a u~: See unscheduled, undeadlined, and undated tasks in my todo files. - Which should then be scheduled or archived. - #+begin_src emacs-lisp -(add-to-list 'org-agenda-custom-commands - '("u" alltodo "" - ((org-agenda-skip-function - (lambda () - (org-agenda-skip-entry-if 'scheduled 'deadline 'regexp "\n]+>"))) - (org-agenda-overriding-header "Unscheduled TODO entries: ")))) +#+end_details + +In any Org file, type ~elisp:menu-set-font~; then you can click on this link to +get a nice font selection menu ---this can be useful for your own ‘personal startup buffer’. + +# Finally, for fun, let's colour all source blocks, in Org mode, by the background colour pink. +# (set-face-attribute 'org-block nil :background "pink") +# MA: This does not work well with dark themes; should use a theme-based setting. + +Let's use the following theme and font, upon startup. +#+begin_src emacs-lisp :tangle init.el +(unless noninteractive + ;; Breaks Gerrit: (my/toggle-font "Roboto Mono Light 14") + (my/toggle-theme 'solarized-gruvbox-light)) #+end_src -/At the end of the day, let's schedule at least 3 things that must be done the -next day; i.e., have priority =A=./ +** Never lose the cursor +:PROPERTIES: +:CUSTOM_ID: Never-lose-the-cursor +:END: -** Tag! You're it! +Let's have the entire line containing the cursour be slightly highlighted. +#+BEGIN_SRC emacs-lisp +;; Make it very easy to see the line with the cursor. +(global-hl-line-mode t) +#+END_SRC + +Moreover, we reduce the mental strain of locating the cursour when navigation happens: +When we switch windows or scroll, for example, we get a wave of light near the cursor. +#+begin_src emacs-lisp +(use-package beacon + :defer nil + :config (setq beacon-color "#666600") + :hook ((org-mode text-mode) . beacon-mode)) +#+end_src + +** Dimming Unused Windows :PROPERTIES: -:CUSTOM_ID: Tag-You're-it +:CUSTOM_ID: Dimming-Unused-Windows :END: -Even when items are categorised under their own parent headings, they may be -related in some way and that can made explicit by adding a ~:tag:~ to their -headings; e.g., two entries both have the ~:jasim:@work:~ tags, then looking for -the ~:@work:~ tag shows me all entries that are tagged as “at work”. +Let's dim windows, and even the whole Emacs frame, when not in use. +#+BEGIN_SRC emacs-lisp +(use-package dimmer + :defer nil + :config (dimmer-mode)) +#+END_SRC - | Tags provide a cross-section of one's entries. | +A more ‘fine-grained’ [[https://github.com/larstvei/Focus][tool]] dims all text except the ‘paragraph’ you're working +on. It's nifty, but not for me. -Tags let us find related stuff quickly, even though they're differently -categorised. +** Flashing when something goes wrong +:PROPERTIES: +:CUSTOM_ID: Flashing-when-something-goes-wrong +:END: -/After calling ~org-agenda~, we may select ~m~ to match for tags, or use -~org-tags-view~ to search for tags./ +Enable flashing mode-line on errors. E.g., ~C-g~, or calling an unbound key +sequence, or misspelling a word. +#+BEGIN_SRC emacs-lisp +;; (setq visible-bell 1) ;; On MacOS, this shows a caution symbol ^_^ -*What to tag?* Common tags are =:@laptop:, :@work:, :@home:= to identify the -location where tasks take place ---Use: When I'm at a particular place, I need -only consider tasks that apply to that place ;-) Other tags I use are =:𝑭𝑳:= to -identify remarks or email or request from person 𝑭irstname 𝑳astname; or -something that might be interesting to that person. I also use ~:video:, :book:, -:paper:~; which let me quickly find all videos! Finally, I also use -=:project_name:= to identify notes that may be of interest to a particular -project, but are more appropriately categorised elsewhere ---e.g., when learning -about an Emacs feature, I may tag my notes with another project's name to -consider whether that feature could be useful there. +;; The doom themes package comes with a function to make the mode line flash on error. +(use-package doom-themes :defer nil) +(require 'doom-themes-ext-visual-bell) +(doom-themes-visual-bell-config) +#+END_SRC -*How to tag?* +-------------------------------------------------------------------------------- -You can just add a ~:tag₁:⋯:tagₙ:~ after a heading. If you press space, before the -tags, then they are automatically indented flushright to column 77; postive -numbers do not flushright but use exact column number. ++A blinking cursor rushes me to type; let's slow down.+ Recently I'm thinking that +a blinking cursours prompts me to continue upwards and onwards. #+BEGIN_SRC emacs-lisp - (setq org-tags-column -77) ;; the default +(blink-cursor-mode 1) #+END_SRC -Use ~C-c C-q~, or ~org-set-tags-command~, on a heading or just the speed key ~:~ on -the asterisks of a heading to set the tags of an item ---as usual, with Helm we -obtain a window of all existing tags to select from. Unfortunatley, this only -supports having one tag; for more, you can add them in manually or … +** Hiding Scrollbar, tool bar, and menu +:PROPERTIES: +:CUSTOM_ID: Hiding-Scrollbar-tool-bar-and-menu +:END: -#+begin_src emacs-lisp -;; Press ‘:’ on a heading to add a tag, press TAB to see all tags, press RETURN on a tag to add it, press TAB again to add more tags, when all done press RETURN twice. -(use-package helm-org) ;; Helm for org headlines and keywords completion. -(add-to-list 'helm-completing-read-handlers-alist - '(org-set-tags-command . helm-org-completing-read-tags)) +As a laptop user, screen space is important, so let's remove rarely used visual +items. +#+BEGIN_SRC emacs-lisp :tangle init.el +(unless noninteractive + (tool-bar-mode -1) ;; No large icons please + (scroll-bar-mode -1)) ;; No visual indicator please + ;; (menu-bar-mode -1) ;; The Mac OS top pane has menu options +#+END_SRC -;; Also provides: helm-org-capture-templates +** Highlight & complete parenthesis pair when cursor is near ;-) +:PROPERTIES: +:CUSTOM_ID: Highlight-complete-parenthesis-pair-when-cursor-is-near +:END: + +Highlight matching ‘parenthesis’ when near one of them. +#+begin_src emacs-lisp :tangle init.el +(setq show-paren-delay 0) +(setq show-paren-style 'mixed) +(show-paren-mode) #+end_src -Now ~:~ or ~C-c C-q~ will show existing tags for the current heading, press ~TAB~ to -obtain a list of all exisiting tags, press ~C-SPC~ to select the desired tags, -then =TAB= or =RET= to confirm the resulting tag list, and ~RET~ to finish or ~TAB~ to -select more tags. +Colour parens, and other delimiters, depending on their depth. +Very useful for parens heavy languages like Lisp. +#+begin_src emacs-lisp :tangle init.el +(use-package rainbow-delimiters + :disabled + :hook ((org-mode prog-mode text-mode) . rainbow-delimiters-mode)) +#+end_src -Let's render tags by Unicode symbols. -#+begin_src emacs-lisp -(use-package org-pretty-tags - :demand t - :config - (setq org-pretty-tags-surrogate-strings - '(("Neato" . "💡") - ("Blog" . "✍") - ("Audio" . "♬") - ("Video" . "📺") - ("Book" . "📚") - ("Running" . "🏃") - ("Question" . "❓") - ("Wife" . "💕") - ("Text" . "💬") ; 📨 📧 - ("Friends" . "👪") - ("Self" . "🍂") - ("Finances" . "💰") - ("Car" . "🚗") ; 🚙 🚗 🚘 - ("Urgent" . "🔥"))) ;; 📥 📤 📬 - (org-pretty-tags-global-mode 1)) +For example: +#+begin_src emacs-lisp :tangle no +(blue (purple (forest (green (yellow (blue)))))) #+end_src -** Automating [[https://en.wikipedia.org/wiki/Pomodoro_Technique][Pomodoro]] ---“Commit for only 25 minutes!” -:PROPERTIES: -:CUSTOM_ID: Automating-https-en-wikipedia-org-wiki-Pomodoro-Technique-Pomodoro-Commit-for-only-25-minutes -:END: +There is a powerful package called ‘smartparens’ for working with pair-able +characters, but I've found it to be too much for my uses. Instead I'll utilise +the lightweight package ~electric~, which Emacs provides out of the box. +#+BEGIN_SRC emacs-lisp :tangle init.el +(electric-pair-mode 1) +#+END_SRC +It supports, by default, ACSII pairs ~{}, [], ()~ and Unicode ~‘’, “”, ⟪⟫, ⟨⟩~. -Effort estimates are for an entire task. -Yet, sometimes it's hard to even get started on some tasks. +When writing Lisp, it is annoyong to have ‘<’ and ‘>’ be completed +/and/ considered as pairs. Let's disassociate them from both notions. +#+BEGIN_SRC emacs-lisp :tangle init.el +;; The ‘<’ and ‘>’ are not ‘parenthesis’, so give them no compleition. +(setq electric-pair-inhibit-predicate + (lambda (c) + (or (member c '(?< ?> ?~)) (electric-pair-default-inhibit c)))) -+ The code below ensures a 25 minute timer is started whenever clocking in happens. +;; Treat ‘<’ and ‘>’ as if they were words, instead of ‘parenthesis’. +(modify-syntax-entry ?< "w<") +(modify-syntax-entry ?> "w>") +#+END_SRC - - The timer is in the lower right of the modeline. +:Rainbow_delims: +#+BEGIN_SRC emacs-lisp :tangle no +;; Act as usual unless a ‘<’ or ‘>’ is encountered. +;; ( char-at is really “character at poisition”; C-h o! ) +(setq rainbow-delimiters-pick-face-function + (lambda (depth match loc) + (unless (member (char-after loc) '(?< ?>)) + (rainbow-delimiters-default-pick-face depth match loc)))) +#+END_SRC +:End: -+ When the timer runs out, we get a notification. +*Adding Org-emphasise markers for pair completion ---Disabled.* -+ We may have the momentum to continue on the difficult task, or clock-out and - take a break after documenting what was accomplished. +Let's add the org-emphasises markers: If we select a word then press =*=, it +becomes bold; likewise for ~/~ for emphasise. +#+BEGIN_SRC emacs-lisp :tangle no +(setq electric-pair-pairs + '((?~ . ?~) + (?* . ?*) + (?/ . ?/))) -#+BEGIN_SRC emacs-lisp -;; Tasks get a 25 minute count down timer -(setq org-timer-default-timer 25) +;; Let's also, for example, avoid obtaining double ‘~’ and ‘/’ when searching for a file. -;; Use the timer we set when clocking in happens. -(add-hook 'org-clock-in-hook - (lambda () (org-timer-set-timer '(16)))) +;; Disable pairs when entering minibuffer +(add-hook 'minibuffer-setup-hook (lambda () (electric-pair-mode 0))) -;; unless we clocked-out with less than a minute left, -;; show disappointment message. -(add-hook 'org-clock-out-hook - (lambda () - (unless (s-prefix? "0:00" (org-timer-value-string)) - (message-box "The basic 25 minutes on this difficult task are not up; it's a shame to see you leave.")) - (org-timer-stop))) +;; Renable pairs when existing minibuffer +(add-hook 'minibuffer-exit-hook (lambda () (electric-pair-mode 1))) #+END_SRC -Note that this does not conflict with the total effort estimate for the task. - -(I'm told there's a package already made for this ---maybe I need to stop writing -code, and do more searches; then again, I've learned a lot by writing code.) - -** Journaling +I use ‘~’ and ‘/’ too much during file navigation, and ‘*’ when marking numerous +Org headers, for which the ‘completed closing pair’ must tiresomely be deleted. +** Proportional fonts for Headlines :PROPERTIES: -:CUSTOM_ID: Journaling -:header-args: :tangle no +:CUSTOM_ID: Proportional-fonts-for-Headlines :END: -*** Intro to why even journal :ignore: +Let's have headings stick out a bit. ++ The larger headings are cute and reminicint of word processors, but having + headings coloured is enough ---the larger size is too much. +#+BEGIN_SRC emacs-lisp +(set-face-attribute 'org-document-title nil :height 2.0) +;; (set-face-attribute 'org-level-1 nil :height 1.0) +;; Remaining org-level-𝒾 have default height 1.0, for 𝒾 : 1..8. +;; +;; E.g., reset org-level-1 to default. +;; (custom-set-faces '(org-level-1 nil)) +#+END_SRC + +Remember you can always use Emacs' Custom utility to get Lisp incantations ;-) +---See notes on Custom above. + +** Making Block Delimiters Less Intrusive :PROPERTIES: -:CUSTOM_ID: Intro-to-why-even-journal +:CUSTOM_ID: Making-Block-Delimiters-Less-Intrusive :END: -Thus far I've made it easy to quickly capture ideas and tasks, not so much on -the analysis phase: - -- What was accomplished today? -- What are some notably bad habits? Good habits? -- What are some future steps? +Let us render Org-mode's ~#+begin_src~ and ~#+end_src~ less obtrusively by, +e.g., having the former render as a pencil marker ~✎~ and the latter as a +tombstone ~□~ ---reminiscent of Halmos' QED end-of-proof marker. +# His setup also accounts for quotes. -Rather than overloading the capture mechanism for such thoughts, let's employ -~org-journal~ ---journal entries are stored in files such as ~journal/20190407~, -where the file name is simply the date, or only one file per year as I've set it -up below. Each entry is the week day, along with the date, then each child tree -is an actual entry with a personal title preceded by the time the entry was -made. Unlike capture and its agenda support, journal ensures entries are -maintained in chronological order with calendar support. +#+begin_details Rasmus’ Incantation +This is from [[https://pank.eu/blog/pretty-babel-src-blocks.html#coderef-symbol][Rasmus Roulund]]. +#+begin_src emacs-lisp + (defvar-local rasmus/org-at-src-begin -1 + "Variable that holds whether last position was a ") -Since org files are plain text files, an entry can be written anywhere and later -ported to the journal. Or, written directly in the journal file if we add the -necessary Org-header: Asterisks and time. + (defvar rasmus/ob-header-symbol ?☰ + "Symbol used for babel headers") -The separation of concerns is to emphasise the capture stage as being quick and -relatively mindless, whereas the journaling stage as being mindful. Even though -we may utilise capture to provide quick support for including journal entries, I -have set my journal to be on a yearly basis ---one file per year--- since I want -to be able to look at previous entries when making the current entry; after all, -it's hard to compare and contrast easily unless there's multiple entries opened -already. + (defun rasmus/org-prettify-src--update () + (let ((case-fold-search t) + (re "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*") + found) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward re nil t) + (goto-char (match-end 0)) + (let ((args (org-trim + (buffer-substring-no-properties (point) + (line-end-position))))) + (when (org-string-nw-p args) + (let ((new-cell (cons args rasmus/ob-header-symbol))) + (cl-pushnew new-cell prettify-symbols-alist :test #'equal) + (cl-pushnew new-cell found :test #'equal))))) + (setq prettify-symbols-alist + (cl-set-difference prettify-symbols-alist + (cl-set-difference + (cl-remove-if-not + (lambda (elm) + (eq (cdr elm) rasmus/ob-header-symbol)) + prettify-symbols-alist) + found :test #'equal))) + ;; Clean up old font-lock-keywords. + (font-lock-remove-keywords nil prettify-symbols--keywords) + (setq prettify-symbols--keywords (prettify-symbols--make-keywords)) + (font-lock-add-keywords nil prettify-symbols--keywords) + (while (re-search-forward re nil t) + (font-lock-flush (line-beginning-position) (line-end-position)))))) -As such, ideally at the end of the day, I can review what has happened, and what -has not, and why this is the case, and what I intend to do about it, and what -problems were encountered and how they were solved ---in case the problem is -encountered again in the future. *Consequently, if I encounter previously -confronted situations, problems,* *all I have to do is reread my journal to get an -idea of how to progress.* Read more about [[https://www.google.com/search?q=on+the+importance+of+reviwing+your+day+daily&oq=on+the+importance+of+reviwing+your+day+daily&aqs=chrome..69i57.367j0j7&sourceid=chrome&ie=UTF-8][the importance of reviewing your day on -a daily basis]]. + (defun rasmus/org-prettify-src () + "Hide src options via `prettify-symbols-mode'. -Moreover, by journaling with Org on a daily basis, it can be relatively easy to -produce a report on what has been happening recently, at work for example. I'd -like to have multiple journals, for work and for personal life, as such I will -utilise a prefix argument to obtain my work specific entries. + `prettify-symbols-mode' is used because it has uncollpasing. It's + may not be efficient." + (let* ((case-fold-search t) + (at-src-block (save-excursion + (beginning-of-line) + (looking-at "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*")))) + ;; Test if we moved out of a block. + (when (or (and rasmus/org-at-src-begin + (not at-src-block)) + ;; File was just opened. + (eq rasmus/org-at-src-begin -1)) + (rasmus/org-prettify-src--update)) + ;; Remove composition if at line; doesn't work properly. + ;; (when at-src-block + ;; (with-silent-modifications + ;; (remove-text-properties (match-end 0) + ;; (1+ (line-end-position)) + ;; '(composition)))) + (setq rasmus/org-at-src-begin at-src-block))) -*** The Setup -:PROPERTIES: -:CUSTOM_ID: The-Setup -:END: + (defun rasmus/org-prettify-symbols () + (mapc (apply-partially 'add-to-list 'prettify-symbols-alist) + (cl-reduce 'append + (mapcar (lambda (x) (list x (cons (upcase (car x)) (cdr x)))) + `(("#+begin_src" . ?✎) ;; ➤ 🖝 ➟ ➤ ✎ + ("#+end_src" . ?□) ;; ⏹ + ("#+header:" . ,rasmus/ob-header-symbol) + ("#+begin_quote" . ?») + ("#+end_quote" . ?«))))) + (turn-on-prettify-symbols-mode) + (add-hook 'post-command-hook 'rasmus/org-prettify-src t t)) -# As of Oct2022, Using org-journal breaks my permissions. E.g., org-time-stamp / C-c . results in " -# directory-files-recursively: Opening directory: Operation not permitted, /Users/musa/Desktop" +;; Last up­dated: 2019-06-09 +#+end_src +#+end_details +#+BEGIN_SRC emacs-lisp +(add-hook 'org-mode-hook #'rasmus/org-prettify-symbols) +(org-mode-restart) +#+END_SRC -Anyhow, the setup: +His development relies on built-in prettify-symbols-mode, which +disguises strings in a buffer for the sake of readability or +aesthetics. Following the example in the documentation, ~C-h f +prettify-symbols-mode~, we can quickly approximate his efforts for +~example~ blocks as follows, however a main issue is that source blocks +have busybodied headers which his setup disguises as ‘≡’. #+begin_src emacs-lisp -(use-package org-journal - ;; C-u C-c j ⇒ Work journal ;; C-c j ⇒ Personal journal - :bind (("C-c j" . my/org-journal-new-entry)) - :config - (setq org-journal-dir "~/Desktop/" ;; "~/Dropbox/journal/" - org-journal-file-type 'yearly - org-journal-file-format "Personal-%Y-%m-%d.org") +(global-prettify-symbols-mode) - (defun my/org-journal-new-entry (prefix) - "Open today’s journal file and start a new entry. +(defvar my/prettify-alist nil + "Musa's personal prettifications.") - With a prefix, we use the work journal; otherwise the personal journal." - (interactive "P") - (let ((org-journal-dir (if prefix "~/Desktop/" org-journal-dir)) - (org-journal-file-format (if prefix "Work-%Y-%m-%d.org" org-journal-file-format))) - (org-journal-new-entry nil) - (org-mode) - (org-show-all)))) -#+end_src - -# Convenient org-journal Snippet Extensions -# Kill journal buffer after saving buffer (By @dhruvparamhans) -# (defun org-journal-save-entry-and-exit() -# "Simple convenience function. -# Saves the buffer of the current day's entry and kills the window -# Similar to org-capture like behavior" -# (interactive) -# (save-buffer) -# (kill-buffer-and-window)) -# (define-key org-journal-mode-map (kbd "C-x C-s") -# 'org-journal-save-entry-and-exit) -# - -*** Super Terse Tutorial -:PROPERTIES: -:CUSTOM_ID: Super-Terse-Tutorial -:END: +(cl-loop for pair in '(;; Example of how pairs like this to beautify org block delimiters + ("#+begin_example" . (?ℰ (Br . Bl) ?⇒)) ;; ℰ⇒ + ("#+end_example" . ?⇐) ;; ⇐ + ;; Actuall beautifications + ("==" . ?≈) ("===" . ?≈) ;; ("=" . ?≔) ;; Programming specific prettifications + ("i32" . ?ℤ) ("u32" . ?ℕ) ("f64" . ?ℝ) ;; Rust specific + ("bool" . ?𝔹) + ;; ("\"\"\"\n" . ?“) ("\"\"\"" . ?”) + ("\"\"\"" . ?“) + ("fn" . ?λ) + ("<=" . ?≤) (">=" . ?≥) + ("->" . ?→) ("-->". ?⟶) ;; threading operators + ("[ ]" . ?□) ("[X]" . ?☑) ("[-]" . ?◐)) ;; Org checkbox symbols + do (push pair my/prettify-alist)) -Bindings available in ~org-journal-mode~, when journaling: -+ ~C-c C-j~: Insert a new entry into the current journal file. - - Note that keys for ~org-journal-new-entry~ shadow those for ~org-goto~. -+ ~C-c C-s~: Search the journal for a string. - - Note that keys for ~org-journal-search~ shadow those for ~org-schedule~. +;; Replace all Org [metadata]keywords with the “▷” symbol; e.g., “#+title: Hello” looks like “▷ Hello”. +(cl-loop for keyword in '(title author email date description options property startup export_file_name html_head fileimage filetags) + do (push (cons (format "#+%s:" keyword) ?▷) my/prettify-alist)) -# C-c C-f - go to the next journal file. -# C-c C-b - go to the previous journal file. +(cl-loop for hk in '(text-mode-hook prog-mode-hook org-mode-hook) + do (add-hook hk (lambda () + (setq prettify-symbols-alist + (append my/prettify-alist prettify-symbols-alist))))) -# Some key-bindings in org-journal overwrite org-mode key bindings -# Major modes are supposed to only use key bindings of the form C-c C-?, where ? -# can be any letter. With org-mode already using most interesting keys, -# collisions are inevitable. Currently, org-journal overwrites -# -# C-c C-f (org-forward-heading-same-level) with org-journal-open-next-entry -# C-c C-b (org-backward-heading-same-level) with org-journal-open-previous-entry -# +(add-hook 'org-mode-hook (lambda () (push '("# " . (?🎶 (Br . Bl) ?\ )) prettify-symbols-alist))) +#+end_src -All journal entries are registered in the Emacs Calendar. To see available -journal entries do ~M-x calendar~. Bindings available in the calendar-mode: +For more on prettify-symbols-mode, read the informative [[https://tony-zorman.com/posts/pretty-latex.html][Prettifying LaTeX Buffers]]. -+ ~j~: View an entry in a new buffer. - # C-j - view an entry but do not switch to it. -+ ~i j~: ‘I’nsert a new ‘j’ournal entry into the day’s file. -+ ~f w/m/y/f/F~: ‘F’ind, search, in all entries of the current week, month, year, all of time, - of in all entries in the future. -# -# [ - go to previous day with journal entries. -# ] - go to next day with journal entries. +#+RESULTS: +:Did_it_work: +#+begin_example lisp +(<= (+ 1 1) (--> 2)) +1 = +2 == +3 === +#+end_example +:End: -All journal entries are registered in the Emacs Calendar. To see available journal entries do M-x calendar. Bindings available in the calendar-mode: -- [[kbd: j d]] display an entry; use [[kbd: j r]] to jump to the new reading buffer; - reading is in doc:view-mode: [[kbd:q]] to quit reading and [[kbd:SPC]] to scroll. -+ [[kbd: j s w/m/y/f]] search the journal entries of the current - week/month/year or for all time -+ ~[/]~ go the previous/next day with journal entries +See [[http://www.modernemacs.com/post/prettify-mode/][“Mathematical Notation in Emacs”]] for how such prettifications can +make verbose (Python) scripts much more readable by employing more +economical disguises. --------------------------------------------------------------------------------- +A nice sanity: +#+BEGIN_SRC emacs-lisp +;; Un-disguise a symbol when cursour is inside it or at the right-edge of it. +(setq prettify-symbols-unprettify-at-point 'right-edge) +#+END_SRC -*** Guided Journaling +** Hiding Emphasise Markers, Inlining Images, and LaTeX-as-PNG :PROPERTIES: -:CUSTOM_ID: Guided-Journaling +:CUSTOM_ID: Hiding-Emphasise-Markers-Inlining-Images-and-LaTeX-as-PNG :END: -Sometimes it can be tough to journal, but filling in a template can be a way to -get started. Later on, we will setup [[Snippets ---Template Expansion]] which will -allow us to write =journal_guided= then ~TAB~ to obtain the template below. Each =$𝓃= -indicates a position that we may input text, after which we ~TAB~ to move to next -location. +:yay_it_worked: -Just like the ~undo-tree~ setup at the start of this read, we use a =noweb-ref= -to present this template in a natural position; then later when template -expansion it setup, we request it to be tangled. +$e^x = \sum_{n = 0}^\infty \frac{x^n}{n!}$ -#+begin_src org :noweb-ref templates-from-other-places-in-my-init :tangle no -,** journal_guided: Introspection & Growth -I'm writing from ${1:location}. +~awkward~ or $not$ -Gut answer, today I feel ${2:scale}/10. -⇒ ${3:Few words or paragraphs to explain what's on your mind.} +:end: -${4: All things which cause us to groan or recoil are part of the tax of -life. These things you should never hope or seek to escape. Life is a battle, -and to live is to fight. +Let's make some things prettier than they appear by default. +#+BEGIN_SRC emacs-lisp +;; org-mode math is now highlighted ;-) +(setq org-highlight-latex-and-related '(latex)) -⟨ Press TAB once you've read this mantra. ⟩ -$(when yas-moving-away-p "") -} -`(progn - (eww "https://www.dailyinspirationalquotes.in/") - (sit-for 2) (when nil let eww load) - (read-only-mode -1) - (goto-line 52) - (kill-line) - (kill-buffer) - (yank))` -${7: -Self Beliefs: -+ I am working on a healthier lifestyle, including a low-carb diet. +;; Extra space between text and underline line +(setq x-underline-at-descent-line t) - - I’m also investing in a healthy, long-lasting relationship. +;; Hide the *,=,/ markers +(setq org-hide-emphasis-markers t) - ➩ These are what I want and are important to me. ⇦ +;; Let’s limit the width of images inlined in org buffers to 400px. +(setq org-image-actual-width 400) -+ I will not use any substances to avoid real issues in my life. I must own them. +;; Visually, I prefer to hide the markers of macros, so let’s do that: +;; {{{go(here)}}} is shown in Emacs as go(here) +(setq org-hide-macro-markers t) -+ Everything I’m searching for is already inside of me. +;; On HTML exports, Org-mode tries to include a validation link for the exported HTML. Let’s disable that since I never use it. +;; (setq org-html-validation-link nil) -+ Progress is more important than perfection. +;; Musa: This is super annoying, in practice. +(setq org-pretty-entities nil) ;; Also makes subscripts (x_{sub script}) and superscripts (x^{super script}) appear in org in a WYSIWYG fashion. +;; to have \alpha, \to and others display as utf8 +;; http://orgmode.org/manual/Special-symbols.html +;; +;; Be default, any consectuive string after “_” or “^” will be shown in WYSIWYG fashion; the following requires “^{⋯}” instead. +;; (setq org-use-sub-superscripts (quote {})) +#+END_SRC +Org pretty entities seems rather impressive ---=M-x org-entities-help= to see all +possibilities, or add your own. I'm already using the Agda input method, so I +wont use Org's ---Agda's gives me a tiny menu narrowing possibilities as I type. +However, it does make subscripts (x_{sub script}) and superscripts (x^{super script}) appear in Org in a WYSIWYG fashion. -⟨ Press TAB once you've read these beliefs. ⟩ -$(when yas-moving-away-p "") -} +-------------------------------------------------------------------------------- -*Three things I'm grateful for:* -1. ${8:??? … e.g., old relationship, something great yesterday, an opportunity I - have today, something simple near me within sight} -2. ${9:??? … e.g., old relationship, something great yesterday, an opportunity I - have today, something simple near me within sight} -3. ${10:??? … e.g., old relationship, something great yesterday, an opportunity I - have today, something simple near me within sight} +Automatically display emphasis markers and links when the cursor is on them. +(c.f. ~fragtog~ below) +#+begin_src emacs-lisp +(use-package org-appear + :disabled t + :hook (org-mode . org-appear-mode) + :init (setq org-appear-autoemphasis t + org-appear-autolinks nil + org-appear-autosubmarkers nil)) +#+end_src -*Three things that would make today great:* -1. ${11:???} -2. ${12:???} -3. ${13:???} +-------------------------------------------------------------------------------- -*What one thing is top of mind today?* -${14:???} +The following is now disabled (yet again, as of Dec/31/2020) ---it makes my system slower than I'd like. +#+BEGIN_SRC emacs-lisp :tangle no +;; Show inline images when loading a new Org file. +(setq org-startup-with-inline-images t) -*What’s one opportunity I want to go after?* -${15:???} +;; Whenever a src block is run, redisplay images so they're up-to-date. +;; Very useful when using ‘ob-latex-as-png’, below. +(add-hook 'org-babel-after-execute-hook #'org-redisplay-inline-images) -*What’s one thing I’m really proud of OR I’m amazed and in awe of?* -${16:???} +;; Automatically convert LaTeX fragments to inline images. +(setq org-startup-with-latex-preview t) +#+END_SRC -$0 -#+end_src +-------------------------------------------------------------------------------- -Besides a bit of webscraping to obtain a daily inspirational quote image, and -the necessary yasnippet code, this template was taken from a discussion on -[[https://news.ycombinator.com/item?id=20849148][Hacker news: “I find journaling indispensable”]]. In time, I will likely alter it -to meet my needs, but I like it as it is right now (•̀ᴗ•́)و +# latex-preview-in-org -** Workflow States -:PROPERTIES: -:CUSTOM_ID: Workflow-States -:END: +Org mode supports inline image previews of LaTeX fragments; e.g., $e^{i \cdot +\pi} - 1 = 0$ or $\substack{𝔹 \\ ↓ \\ 𝒜}$. These can be toggled with +kbd:C-c_C-x_C-l. [[https://github.com/io12/org-fragtog][Org-fragtog]] automates this, so fragment previews are disabled +for editing when your cursor steps onto them, and re-enabled when the cursor +leaves. -Here are some of my common workflow states, ---the ‘X/Y’ indicates to do action ‘X’ -when entering a state and ‘Y’ when leaving it, with ‘!’ denoting a timestamp -should be generated and ‘@’ denoting a user note should be made. -#+begin_src emacs-lisp -(setq org-todo-keywords - '((sequence "TODO(t)" "STARTED(s@/!)" "|" "DONE(d/!)") - (sequence "WAITING(w@/!)" "ON_HOLD(h@/!)" "|" "CANCELLED(c@/!)"))) + #+BEGIN_SRC emacs-lisp +;; Automatically toggle LaTeX previews when cursour enters/leaves them +(use-package org-fragtog + :disabled t + :hook (org-mode . org-fragtog-mode)) + #+END_SRC -;; Since DONE is a terminal state, it has no exit-action. -;; Let's explicitly indicate time should be noted. -(setq org-log-done 'time) -#+end_src +doc:org-latex-preview, kbd:C-c_C-x_C-l, renders ~$e^{i \pi} + 1 = 0$~ into a +really nice inline image: $e^{i \pi} + 1 = 0$. It also works for LaTeX +environments ---for personal environments, just ~(add-to-list +'org-latex-packages-alist "LaTeX definitions here")~. -The ~@~ brings up a pop-up to make a local note about why the state changed. -*Super cool stuff!* +#+begin_src emacs-lisp +;; Make previews a bit larger +(setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5)) -In particular, we transition from ~TODO~ to ~STARTED~ once 15 minutes, or a -reasonable amount, of work has transpired. Since all but one state are marked -for logging, we could use the ~lognotestate~ logging facility of org-mode, which -prompts for a note every time a task’s state is changed. +;; I use a lot of Unicode, so let's always include a unicode header. +(maybe-clone "https://armkeh.github.io/unicode-sty/") +(setq org-format-latex-header + (concat org-format-latex-header + "\n\\usepackage{\\string~\"/unicode-sty/unicode\"}")) +;; +;; Now this looks nice too! +;; $\substack{𝔹 \\ ↓ \\ 𝒜}$ and $\mathbb{B}$. -Entering a comment about what I've done, even if it's very little, feels like -I'm getting something done. It's an explicit marker of progress and motivates me -to want to change my task's states more often until I see it marked ~DONE~. +;; Always support unicode upon LaTeX export +;; No need to explicitly import armkeh's unicode-sty in each org file. +(add-to-list 'org-latex-packages-alist + "\n\\usepackage{\\string~\"/unicode-sty/unicode\"}") +#+end_src -Here's how they are coloured, -#+begin_src emacs-lisp -(setq org-todo-keyword-faces - '(("TODO" :foreground "red" :weight bold) - ("STARTED" :foreground "blue" :weight bold) - ("DONE" :foreground "forest green" :weight bold) - ("WAITING" :foreground "orange" :weight bold) - ("ON_HOLD" :foreground "magenta" :weight bold) - ("CANCELLED" :foreground "forest green" :weight bold))) -#+end_src +This approach does not work well for forming diagrams; I've tried to make tikzcd +work this way and failed. Using ~ob-latex-as-png~ as a substitute. +# work this way and failed. Using [doc : org-babel-execute:latex-as-png][ob-latex-as-png] as a substitute. +:calc: +#+begin_src emacs-lisp :tangle no +;; \step should be local to \begin{calc}⋯\end{calc}! +(add-to-list 'org-latex-packages-alist +"\\def\\BEGINstep{ \\{ } +\\def\\ENDstep{ \\} } +\\newcommand{\\step}[2][=]{ \\\\ #1 \\;\\; & \\qquad \\color{maroon}{\\BEGINstep \\text{ #2 } \\ENDstep} \\\\ & } +\\newenvironment{calc}{\\begin{align*} & }{\\end{align*}}") -Now we press ~C-c C-t~ then the letter shortcut to actually make the state of an org heading. -#+begin_src emacs-lisp -(setq org-use-fast-todo-selection t) +; (pop org-latex-packages-alist) + +;; See also org-format-latex-header #+end_src +:End: -We can also change through states using Shift- left, or right. -# (setq org-treat-S-cursor-todo-selection-as-state-change nil) +# LaTeX Rendering: Support “latex-as-png” src blocks, which show LaTeX as PNGs +# LaTeX-Rendering-Support-latex-as-png-src-blocks-which-show-LaTeX-as-PNGs -Let's draw a state diagram to show what such a workflow looks like. + #+BEGIN_SRC emacs-lisp +;; Support “latex-as-png” src blocks, which show LaTeX as PNGs +(use-package ob-latex-as-png :disabled t) + #+END_SRC -[[http://plantuml.com/index][PlantUML]] supports drawing diagrams in a tremendously simple format ----it even supports Graphviz/DOT directly and many other formats. -Super simple setup instructions can be found [[http://eschulte.github.io/babel-dev/DONE-integrate-plantuml-support.html][here]]; below are a bit more -involved instructions. Read the manual [[http://plantuml.com/guide][here]]. +-------------------------------------------------------------------------------- -#+begin_src emacs-lisp -;; Install the tool -; (async-shell-command "brew tap adoptopenjdk/openjdk; brew cask install adoptopenjdk13") ;; Dependency -; (async-shell-command "brew install plantuml") +Use ~ref:my-stuff~ to refer to an Org entity with ~#+name: my-stuff~; which must +have a ~#+caption: ⋯~ as well. Example entities include tables and source +blocks; as well as figure blocks. For equation blocks, you must use a +~\label{⋯}~ directly. -;; Tell emacs where it is. -;; E.g., (async-shell-command "find / -name plantuml.jar") -(setq org-plantuml-jar-path - "/usr/local/Cellar/plantuml/1.2022.14/libexec/plantuml.jar") + #+begin_src emacs-lisp +;; Use the “#+name” the user provides, instead of generating label identifiers. +(setq org-latex-prefer-user-labels t) + #+end_src -;; Enable C-c C-c to generate diagrams from plantuml src blocks. -(add-to-list 'org-babel-load-languages '(plantuml . t) ) -(require 'ob-plantuml) +** Show off-screen heading at the top of the window +:PROPERTIES: +:CUSTOM_ID: Show-off-screen-heading-at-the-top-of-the-window +:END: -; Use fundamental mode when editing plantuml blocks with C-c ' -(add-to-list 'org-src-lang-modes '("plantuml" . fundamental)) -#+end_src -# -# (async-shell-command "cp workflow.png ~/alhassy.github.io/assets/img/") +In case we forgot which heading we're under, let's keep +the current heading stuck at the top of the window. +#+BEGIN_SRC emacs-lisp + (use-package org-sticky-header + :defer nil + :hook (org-mode . org-sticky-header-mode) + :config + (setq-default + org-sticky-header-full-path 'full + ;; Child and parent headings are seperated by a /. + org-sticky-header-outline-path-separator " / ")) +#+END_SRC +** Powerful Directory Editing with ~dired :Disabled:~ +:PROPERTIES: +:CUSTOM_ID: Powerful-Directory-Editing-with-dired +:header-args: :tangle no +:END: -Let's use this! -# The source block is replaced with the generated image in-place, by default. -# #+begin_src plantuml :file workflow.png :exports code :cache (progn (async-shell-command "cp workflow.png ~/alhassy.github.io/assets/img/") "yes") -#+begin_src plantuml :file images/workflow.png :tangle no :exports both :eval never-export :results replace -skinparam defaultTextAlignment center /' Text alignment '/ +⟨ ~C-x C-v~ to open a file or directory in dired, using the current buffer. ⟩ -skinparam titleBorderRoundCorner 15 -skinparam titleBorderThickness 2 -skinparam titleBorderColor red -skinparam titleBackgroundColor Aqua-CadetBlue -title My Personal Task States +As mentioned earlier, ~dired~ is Emacs' built-in directory editor; it's opened +with ~C-x d~. /Dired let's us treat directories as textual objects!/ In dired, +press ~h~ to see the many actions available. Here's a few... -[*] -> Todo /' This is my starting state '/ -Done -right-> [*] /' This is an end state '/ -Cancelled -up-> [*] /' This is an end state '/ +#+begin_details Super Terse ‘dired’ Tutorial -/'A task is “Todo”, then it's “started”, then finally it's “done”. '/ -Todo -right-> Started -Started -down-> Waiting -Waiting -up-> Started -Started -right-> Done ++ ~(~ toggles hiding entry details, such as modification date and ownership ++ ~s~ sort entries; modeline will display “Dired by date” or “Dired by name”. ++ ~o~ to open entry in anOther window; or ~RET~ to open in place. ++ ~+~ to create a new directory; or ~M-x make-directory~. ++ ~/~ to filter entries; with ~which-key~, possible completions pop-up. + - E.g., ~/ f~ shows only files or ~/ . png~ to obtain all entries with extension + ~png~. + - ~/ i g~ to hide git-ignored items ^_^ + - ~/ /~ to remove all filters. ++ ~TAB~ to navigate between different groupings of entries. + - ~RET~ on a drawer heading toggles folding it ^_^ +#+end_details -/'Along the way, I may pause the task for some reason then - return to it. This may be since I'm “Blocked” since I need - something, or the task has been put on “hold” since it may not - be important right now, and it may be “cancelled” eventually. -'/ +The [[https://github.com/Fuco1/dired-hacks#dired-hacks-utils][dired-hacks]] family of packages lets us, say, get a dired buffer out of a shell +incantation that lists files, or use dired to open files with external tools. +Below we use three of its packages. -Todo -down-> Waiting -Waiting -up-> Todo -Waiting -up-> Done +Pressing ~i~ inserts a directory's children under it, indented, in the current +buffer. Useful to see what's there. +#+BEGIN_SRC emacs-lisp +(use-package dired-subtree + :defer nil + :bind (:map dired-mode-map + ("i" . dired-subtree-toggle))) +#+END_SRC -Todo -down-> On_Hold -On_Hold -> Todo +When directory ~𝒳~ has only one child ~𝒴~, then in dired, instead of ~𝒳~, show me ~𝒳/𝒴~ +with ~𝒳~ greyed out. +#+BEGIN_SRC emacs-lisp +(use-package dired-collapse + :defer nil + :hook (dired-mode . dired-collapse-mode)) +#+END_SRC -On_Hold -down-> Cancelled -Waiting -down-> Cancelled -Todo -down-> Cancelled +Begin dired with certain entries grouped together, according to some filtering +requirement; and with “garbage” files not shown ---i.e., those ending in +~.aux, .out~, etc. +#+BEGIN_SRC emacs-lisp +(use-package dired-filter + :defer nil + :hook (dired-mode . (lambda () (dired-filter-group-mode) + (dired-filter-by-garbage))) + :custom + (dired-garbage-files-regexp + "\\(?:\\.\\(?:aux\\|bak\\|dvi\\|log\\|orig\\|rej\\|toc\\|out\\)\\)\\'") + (dired-filter-group-saved-groups + '(("default" + ("Org" (extension "org")) + ("Executables" (exexutable)) + ("Directories" (directory)) + ("PDF" (extension "pdf")) + ("LaTeX" (extension "tex" "bib")) + ("Images" (extension "png")) + ("Code" (extension "hs" "agda" "lagda")) + ("Archives"(extension "zip" "rar" "gz" "bz2" "tar")))))) +#+END_SRC -/' The Org-mode shortcuts for these states are as follows. '/ -Todo : t -On_Hold : h -Started : s -Waiting : w -Cancelled : c -Done : d +#+begin_details [Disabled] Neotree: Traditional Directory Tree Navigation +link-here:Neotree-Traditional-Directory-Tree-Navigation -/' If a task is paused, we should document why this is the case. '/ -note right of Waiting: Note what is\nblocking us. -note right of Cancelled: Note reason\nfor cancellation. -note bottom of On_Hold: Note reason\nfor reduced priority. +We open a nifty file manager upon startup. +#+BEGIN_SRC emacs-lisp :tangle no +;; Sidebar for project file navigation +(use-package neotree -center footer ♥‿♥ Org-mode is so cool (•̀ᴗ•́)و -/' Note that we could omit the “center, left, right” if we wished, - or used a “header” instead.'/ -#+end_src + :disabled + :config (global-set-key "\C-x\ d" 'neotree-toggle) + (setq neo-theme 'icons)) ;; Uses all-the-icons from § Booting Up -#+RESULTS: -[[file:images/workflow.png]] +;; Open it up upon startup. +;; (neotree-toggle) +#+END_SRC +By default ~C-x d~ invokes ~dired~, but I prefer ~neotree~ for file +management. -# (org-display-inline-images t t) -# (shell-command "rm workflow.png") -# +HTML: My Personal Task States + ⟨ Edit: As a naive user, this is what I thought; yet a year later, + I've almost never used neotree. ⟩ -Of note: +Useful navigational commands include ++ ~U~ to go up a directory. ++ ~C-c C-c~ to change directory focus; ~C-C c~ to type the directory out. ++ ~?~ or ~h~ to get help and ~q~ to quit. -+ Multiline comments are with ~/' comment here '/~, single quote starts a one-line comment. +As always, to go to the neotree pane when it's the only other window, +execute ~C-x o~. -+ Nodes don't need to be declared, and their names may contain spaces if they are enclosed in double-quotes. +I /rarely/ make use of this feature; company mode & Helm together quickly provide +an automatic replacement for nearly all of my uses. -+ One forms an arrow between two nodes by writing a line with ~x ->[label here] y~ - or ~y <- x~; or using ~-->~ and ~<--~ for dashed lines. The label is optional. ++ Reminiscent of GUI file managers is [[https://github.com/ralesi/ranger.el#features][ranger]]; e.g., it has multi-column + display of parent directories along with a file preview mechanism. +#+end_details - To enforce a particular layout, use ~-X->~ where ~X ∈ {up, down, right, left}~. +** Persistent Scratch Buffer +:PROPERTIES: +:CUSTOM_ID: Persistent-Scratch-Buffer +:END: -+ To declare that a node ~x~ has fields ~d, f~ we make two new lines having - ~x : f~ and ~x : d~. +The ~*scratch*~ buffer is a nice playground for temporary data or experiments. -+ One adds a note near a node ~x~ as follows: ~note right of x: words then newline\nthen more words~. +However, by default its contents are not saved --which may be an issue if we +have not relocated our playthings to their appropriate files. Whence let's save +& restore the scratch buffer by default. +#+BEGIN_SRC emacs-lisp +(use-package persistent-scratch - Likewise for notes on the ~left, top, bottom~. + ;; In this mode, the usual save key saves to the underlying persistent file. + :bind (:map persistent-scratch-mode-map + ("C-x C-s" . persistent-scratch-save))) +#+END_SRC - - A note can be on several lines. It's terminated by ~end note~. +We might accidentally close this buffer, so we could utilise the following. +#+BEGIN_SRC emacs-lisp +(defun scratch () + "Recreate the scratch buffer, loading any persistent state." + (interactive) + (switch-to-buffer-other-window (get-buffer-create "*scratch*")) + (condition-case nil (persistent-scratch-restore) (insert initial-scratch-message)) + (org-mode) + (persistent-scratch-mode) + (persistent-scratch-autosave-mode 1)) -+ Interesting sprites and many other things can be done with PlantUML. Read the docs. +;; This doubles as a quick way to avoid the common formula: C-x b RET *scratch* -This particular workflow is inspired by [[http://doc.norang.ca/org-mode.html][Bernt Hansen]] ---while quickly searching -through the PlantUML [[http://plantuml.com/guide][manual]]: The above is known as an “activity diagram” and -it's covered in §4. - -Org-mode may be used with PlantUML: -+ See §11,12 for using Org-mode notation to form ‘mindmaps’ and ‘work breakdown - structures’. - -+ Org-mode text formatters are also acknowledged but the delimiters must be - doubled; see §16.1. +;; Upon startup, close the default scratch buffer and open one as specfied above +(ignore-errors (kill-buffer "*scratch*") (scratch)) +#+END_SRC -You can quickly write and see the resulting UMLs using -https://liveuml.com/, for the most part. +I use Org-mode often, so that's how I want things to appear. +#+BEGIN_SRC emacs-lisp +(setq initial-scratch-message (concat + "#+title: Persistent Scratch Buffer" + "\n#\n# Welcome! This’ a place for trying things out." + "\n#\n# ⟨ ‘C-x C-s’ here saves to ~/.emacs.d/.persistent-scratch ⟩ \n\n")) +#+END_SRC -** Clocking Work Time +** Tabs :Disabled: :PROPERTIES: -:CUSTOM_ID: Clocking-Work-Time +:CUSTOM_ID: Tabs +:header-args: :tangle no :END: -Let's keep track of the time we spend working on tasks that we may have captured -for ourselves the previous day. Such statistics provides a good idea of how -long it actually takes me to accomplish a certain task in the future and it lets -me know where my time has gone. - -+ Clock in :: on a heading with ~I~, or in the subtree with ~C-c C-x C-i~. -+ Clock out :: of a heading with ~O~, or in the subtree with ~C-c C-x C-o~. -+ Clock report :: See clocked times with ~C-c C-x C-r~. - -After clocking out, the start and end times, as well as the elapsed time, are -added to a drawer to the heading. We can punch in and out of tasks as many times -as desired, say we took a break or switched to another task, and they will all -be recorded into the drawer. - -#+begin_src emacs-lisp -;; Record a note on what was accomplished when clocking out of an item. -(setq org-log-note-clock-out t) -#+end_src - -To get started, we could estimate how long a task will take and clock-in; then -clock-out and see how long it actually took. - -# To review the day’s accomplishments, type ‘l’ -# (org-agenda-log-mode) from the agenda view. - -Sometimes, at the beginning at least, I would accidentally invoke the transposed -command ~C-x C-c~, which saves all buffers and quits Emacs. So here's a helpful -way to ensure I don't quit Emacs accidentally. -#+begin_src emacs-lisp -(setq confirm-kill-emacs 'yes-or-no-p) -#+end_src - -A few more settings: -#+begin_src emacs-lisp -;; Resume clocking task when emacs is restarted -(org-clock-persistence-insinuate) +I really like my Helm-supported ~C-x b~, but the visial appeal of a [[https://github.com/manateelazycat/awesome-tab][tab bar]] for Emacs +is interesting. Let's try it out and see how long this lasts ---it may be like Neotree: +Something cute to show to others, but not as fast as the keyboard. -;; Show lot of clocking history -(setq org-clock-history-length 23) +#+BEGIN_SRC emacs-lisp :tangle no +(use-package awesome-tab + :disabled t + :quelpa (awesome-tab :fetcher git :url "https://github.com/manateelazycat/awesome-tab.git") + :config (awesome-tab-mode t)) -;; Resume clocking task on clock-in if the clock is open -(setq org-clock-in-resume t) +;; Show me /all/ the tabs at once, in one group. +(defun awesome-tab-buffer-groups () + (list (awesome-tab-get-group-name (current-buffer)))) +#+END_SRC -;; Sometimes I change tasks I'm clocking quickly ---this removes clocked tasks with 0:00 duration -(setq org-clock-out-remove-zero-time-clocks t) +It's been less than three days and I've found this utility to be unhelpful, to me anyhow. -;; Clock out when moving task to a done state -(setq org-clock-out-when-done t) +An alternative is [[https://github.com/ema2159/centaur-tabs][centaur-tabs.]] -;; Save the running clock and all clock history when exiting Emacs, load it on startup -(setq org-clock-persist t) +** Window resizing using the golden ratio :Disabled: +:PROPERTIES: +:CUSTOM_ID: Window-resizing-using-the-golden-ratio +:header-args: :tangle no +:END: -;; Do not prompt to resume an active clock -(setq org-clock-persist-query-resume nil) +Let's load the following package, which automatically resizes windows so that +the window containing the cursor is the largest, according to the golden ratio. +Consequently, the window we're working with is nice and large yet the other windows +are still readable. -;; Include current clocking task in clock reports -(setq org-clock-report-include-clocking-task t) +#+begin_src emacs-lisp :tangle no +(use-package golden-ratio + :disabled t + :init (golden-ratio-mode 1)) #+end_src -*** Finding tasks to clock in +After some time this got a bit annoying and I'm no longer using this. + +** Org-Emphasise for Parts of Words :Disabled: :PROPERTIES: -:CUSTOM_ID: Finding-tasks-to-clock-in +:CUSTOM_ID: Org-Emphasise-for-Parts-of-Words +:header-args: :tangle no :END: -Use one of the following options, with the top-most being the first to be tried. -+ From anywhere, ~C-u C-c C-x C-i~ yields a pop-up for recently clocked in tasks. -+ Pick something off today's agenda scheduled items. -+ Pick a ~Started~ task from the agenda view, work on this unfinished task. -+ Pick something from the ~TODO~ tasks list in the agenda view. -# Reporting activities -# C-c C-x i +From [[https://stackoverflow.com/a/24540651/3550444][stackoverflow]], the following incantation allows us to have +parts of works emphasied with org-mode; e.g., +/half/ed, ~half~ed, and right in the m*idd*le! Super cool stuff! +#+BEGIN_SRC emacs-lisp :tangle no +(setcar org-emphasis-regexp-components " \t('\"{[:alpha:]") +(setcar (nthcdr 1 org-emphasis-regexp-components) "[:alpha:]- \t.,:!?;'\")}\\") +(org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components) +#+END_SRC -~C-c C-x C-d~ also provides a quick summary of clocked time for the current org file. +I've disabled this feature since multiple occurrences +of an emphasise marker are sometimes treated as one +lengthy phrase being emphasised. -*** Estimates versus actual time +** Preview link under cursor :Disabled: :PROPERTIES: -:CUSTOM_ID: Estimates-versus-actual-time +:CUSTOM_ID: Preview-link-under-cursor +:header-args: :tangle no :END: -Before clocking into a task, add to the properties drawer ~:Effort: 1:25~ or ~C-c -C-x C-e~, for a task that you estimate will take an hour and twenty-five minutes, -for example. Now the modeline will mention the time elapsed alongside the task -name. *Woah!* +When cursor sits on a URL/Image/File link, try to preview it in a tooltip. +- Useful to quickly preview files and images. +- See also: https://github.com/jcs-elpa/preview-it #+begin_src emacs-lisp - (push '("Effort_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00") - org-global-properties) +(quelpa '(preview-it :repo "jcs-elpa/preview-it" :fetcher github)) +;; (global-preview-it-mode) #+end_src -#+begin_quote org -Use speed keys ~e/E~ to insert an effort estimate, with the above being provided - options, or to increment the current effort to the next one in the above list. -#+end_quote - -This is also useful when you simply want to put a time limit on a task that - wont be completed anytime soon, say writing a thesis or a long article, but - you still want to work on it for an hour a day and be warned when you exceed - such a time constraint. - - :Not_working_for_me: - Even if you switch tasks then clock into this task again, the alarm will ring - again, nagging you to actual listen to yourself and work on other matters. +This also works nicely when I'm looking to open a file; e.g., ~C-x C-f +~/.emacs.d/.as TAB~ to preview my ~.aspell.en.pws~ (Emacs personal dictionary) file +in a tooltip. +# (Sadly, the preview closes my ~M-x~ session when previewing an org file.) +# Not entirely true; but it's happened with some org files. - #+begin_src emacs-lisp -(setq org-clock-sound "~/.emacs.d/school-bell.wav") +Likewise, I'd like to preview line when executing the doc:goto-line / kbd:M-g_M-g command. +#+begin_src emacs-lisp +(quelpa '(goto-line-preview :repo "jcs-elpa/goto-line-preview" :fetcher github)) +(global-set-key [remap goto-line] 'goto-line-preview) #+end_src - :end: - When you've gone above your estimate time, the modeline colours it red. - -** Habit Formation +** Replace phrases with nice SVG labels :Disabled:Causes_Org_rendering_issues: :PROPERTIES: -:CUSTOM_ID: Habit-Formation +:CUSTOM_ID: Replace-phrases-with-nice-SVG-labels +:header-args: :tangle no :END: -/The/ reason to use habits is that they come with a graph indicating consistency -by colour, and the goal of the game is to have [[https://lifehacker.com/jerry-seinfelds-productivity-secret-281626][the longest possible chain]] ---no -red days! +:TODO_What_kind_of_Org_rendering_issues_are_caused: +Some org headings are not fontified, and Org links are not fontified; e.g., -A ‘habit’ is a usual (recurring) todo task marked as a habit: -Use =C-c C-x p= to set the =STYLE= property to =habit= on a task to set it as a habit. + 📆 [[https://calendar.google.com/calendar/u/0/r][Calendar]] ✉️ -#+BEGIN_SRC emacs-lisp -;; Show habits for every day in the agenda. -(setq org-habit-show-habits t) -(setq org-habit-show-habits-only-for-today nil) ++ I have to copy-paste it to get it to fontify. ++ Likewise, Org headings need to be modified (e.g. pressing `t` to make them todo) to get them fontified. +:End: -;; This shows the ‘Seinfeld consistency’ graph closer to the habit heading. -(setq org-habit-graph-column 90) +[[ https://github.com/rougier/svg-tag-mode][SVG tags mode]] let's us replase arbitrary regular expressions with beautiful SVG +images that can be /clicked/ to produce an action, and may have a tooltip to +provide contextual information. Essentially an alternative to the built-in +doc:font-lock-mode, which performs arbitrary syntax highlighting. +- For more power, use the ~svg-lib~ package. +- The docs have nice examples. [[https://github.com/rougier/svg-tag-mode/blob/main/examples/example-2.el][Here]] are more useful examples. -;; In order to see the habit graphs, which I've placed rightwards, let's -;; always open org-agenda in ‘full screen’. -;; (setq org-agenda-window-setup 'only-window) -#+END_SRC +# Below I setup a function, doc : my/svg-tag-declare-badge to /declaratively/ produce SVG badges. -| /inch by inch anything's a cinch!/ | +#+begin_src emacs-lisp +(use-package svg-tag-mode + :defer nil + :hook (org-mode prog-mode) + ;; :config (global-svg-tag-mode) ;; Nope: Breaks xwidget-webkit-browse-url, issue#28. + :config + (cl-defun my/svg-tag-declare-badge (template face &optional tooltip-message-upon-hover) + ;; Example faces: 'org-level-1 'org-todo 'font-lock-doc-face + "Given a TEMPLATE of the shape \"𝑿❙𝒀\", make SVG badge whose tag is 𝑿 and label is 𝒀. -~!~ means today and ~⋆~ means a task has been done on that day; -intuitively green means you're on track, yellow is warning sign of overdue, -red is overdue, and blue is an acceptable break day. + When `svg-tags-mode' is enabled, every occurence of \"\\(𝑿\\)\\(𝒀\\)\" + is replaced by an SVG image essentially displaying “[𝑿∣𝒀]” using the given FACE. + This badge can be clicked to show all instances in the buffer. + You can see the badges documentation / intentions / help-message when you hover over it; + to see TOOLTIP-MESSAGE-UPON-HOVER. -Here's an example habit from the [[https://orgmode.org/manual/Tracking-your-habits.html][Org-mode manual]], where ~.+𝒳d/𝒴d~ reads -/perform the habit once every 𝒳 days, but never let me go 𝒴 days without doing it./ -#+begin_example org -,** TODO Shave - SCHEDULED: <2020-01-08 Wed .+2d/4d> - :PROPERTIES: - :STYLE: habit - :END: -#+end_example -/Shave every 2 days, but we can take a 3-day break; however, on the 4th day, -gotta shave!/ (To “ignore” a habit, just reschedule it for another day.) + Both 𝑿 and 𝒀 are regeular expressions; “❙” serves as the SVG tag-label delimiter + ---i.e., it saves as from writing \"\\(𝑿\\)\\(𝒀\\)\". Moreover, the SVG is only active + when regexp \"\\(𝑿\\)\\(𝒀\\)\" matches an instance." -Remember that in the agenda view if you alter a task, say with ~t~ to mark it -done, then you need to use ~s~ to save the underlying todo/notes files; otherwise, -any ~g~ will revert the change in the agenda buffer. + ;; Append tooltip message with a notice on what happens upon click. + (--> "Click on me to all see occurrences of this badge, in the current buffer!" + (if tooltip-message-upon-hover (concat tooltip-message-upon-hover "\n\n" it) it) + (setq tooltip-message-upon-hover it)) -** Actually Doing Things ---or /Sending notifications from Emacs/ -:PROPERTIES: -:CUSTOM_ID: Actually-Doing-Things -:END: + (-let [(tag label) (s-split "❙" template)] + (-let [click-to-show-all-buffer-occurrences `(lambda () (interactive) (occur (concat ,tag ,label)))] + ;; Make an SVG for the tag. + (push + (cons (format "\\(%s\\)%s" tag label) `((lambda (tag) (svg-tag-make (s-chop-suffix ":" (s-chop-prefixes '("[" "<" "/*") tag)) :face (quote ,face) :inverse t :margin 0 :crop-right t :crop-left nil)) + ,click-to-show-all-buffer-occurrences + ,tooltip-message-upon-hover)) + svg-tag-tags) + ;; Make an SVG for the label. + (push + (cons (format "%s\\(%s\\)" tag label) `((lambda (label) (svg-tag-make (s-chop-suffixes '("]" ">" "*/") label) :face (quote ,face) :crop-left t)) + ,click-to-show-all-buffer-occurrences + ,tooltip-message-upon-hover)) + svg-tag-tags)))) -Let's setup a little audio-visual reminder to regularly check my agenda -and ensure I'm not narrowing on a single task and ignoring others. + ;; Let's start off empty; then declare badges below. + (setq svg-tag-tags nil) -+ More generally, we can use this snippet of code to let Emacs notify us of - other things. + ;; Using caps so that these stick-out somewhat even when svg-tags-mode is not present. + (my/svg-tag-declare-badge "TODO:❙.*" 'org-todo "This is something I would like to do, in the future.") + (my/svg-tag-declare-badge "SILLY:❙.*" 'error "I’m experimenting; don't forget to clean-up when you’re done!") + (my/svg-tag-declare-badge "HACK:❙.*" 'error "This works, but it’s far from ideal. Plan to clean this in the future.") + (my/svg-tag-declare-badge "FIXME:❙.*" 'org-todo "This is busted! Plan to fix this in the future.") + (my/svg-tag-declare-badge "NOTE:❙.*" 'org-done "Something to be aware of; to keep in mind.") -+ For instance, the doc:message function shows text in the minibuffer, which - might be missed when there are multiple incoming messages or focus is on a - non-Emacs application (gasp!). Then, doc:my/notify could be used to produce - MacOS system-wide notifications. + ;; [In]Active Time stamps --- M-x org-time-stamp + (my/svg-tag-declare-badge "\\[2022-.* ❙.*]" 'org-done "This is an inactive time stamp. It does not trigger the parent entry to appear in the agenda.") + (my/svg-tag-declare-badge "<2022-.* ❙.*>" 'org-todo "This is an active time stamp. It causes the parent Org entry to appear in the agenda.") -The [[https://askubuntu.com/a/501917][text-to-speech tool]] we'll use is ~say~; which [[https://www.lifewire.com/mac-say-command-with-talking-terminal-2260772#:~:text=In%20the%20left%20pane%2C%20select,voices%20your%20Mac%20can%20use.][on a Mac can be activated]] -in a browser: Select some text, right-click, select ~Speech~, then ~Start/Stop Speaking~. -- TODO: Make the command below randomly use an English speaking voice; “tldr say” to learn more. -- TODO: Also finish reading the above mentioned links, with nice examples. + ;; JavaScript Lint Rules: \* eslint (.*) */ + (my/svg-tag-declare-badge "/\\* eslint ❙.* \\*/" 'org-done "It looks like you’ deviating from common conventions: Tread cautiously!") -#+begin_src emacs-lisp -;; Obtain a notifications and text-to-speech utilities -(system-packages-ensure "say") ;; Built-into MacOS, but can be downloaded in Ubuntu -(system-packages-ensure "terminal-notifier") ;; MacOS specific -;; System Preferences → Notifications → Terminal Notifier → Allow “alerts”. -;; E.g.,: (shell-command "terminal-notifier -title \"Hiya\" -message \"hello\"") -#+end_src + ;; TODO: Make SVG tags for other interesting “2-part” pieces of textual information + ) -By default, notifications are in banner style ---they go away automatically--- -we can use alert style ---in which they stay until dismissed--- in MacOS as -follows: =System Preferences → Notifications → terminal-notifier → Alerts=. +;; If everything is setup, the following examples should look like SVGs. +;; NOTE: Do something +;; TODO: fix me later +;; HACK: hiya +;; FIXME: this thing is busted 🎭 +;; SILLY: start +;; SILLY: end +;; [2022-04-20 Sun 16:30] +;; <2022-04-20 Sun 16:30> +;; /* eslint eqeqeq: 0, curly: 2 */ -#+begin_src emacs-lisp -(cl-defun my/notify (message &key (titled "") at repeat-every-hour open) - "Notify user with both an visual banner, with a beep sound, and a text-to-speech recitation. +;; NOTE: Toggle svg-tags-mode; useful when experimenting with new tags. +;; (progn (svg-tag-mode-off) (svg-tag-mode-on)) -When the user clicks on the resulting notification, unless a -given OPEN url is provided, the Emacs application is brough into -focus. +;; NOTE: (my/toggle-line-fontification) works fine with svg-tag-mode :-) +#+end_src -MESSAGE and TITLE are strings; AT is a time string as expected of -`run-at-time' such as \"11.23pm\" or \"5 sec\"; REPEAT-EVERY-HOUR -is a floating-point number of hours to continuously repeat the -alert. OPEN is a URL that is opened when the user clicks the -notification. This can be a web or file URL, or any custom URL -scheme. +** Now C-c C-x C-v shows remote images inline, neato! +#+begin_src emacs-lisp :tangle init.el + (quelpa '(org-remoteimg :fetcher github :repo "gaoDean/org-remoteimg")) + (require 'org-remoteimg) + (setq url-cache-directory "~/emacs.d/.cache/") + (setq org-display-remote-inline-images 'cache) +#+end_src -I initially used optional arguments, but realised that in due time -it would be more informative to use named arguments instead. +Example: +[[https://www.javacodegeeks.com/wp-content/uploads/2017/09/lexer-parser-center.png][lexing vs parsing]] -Example uses: -;; In 5 minutes from now, remind me to watch this neato video! -(my/notify \"🔔 Get things done! 📎 💻 \" - :open \"https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity\" - :at \"5 minutes\") +[[https://miro.medium.com/v2/resize:fit:4800/1*bpG6TxN-nS6enHkp-KIZgg.jpeg][1% better everyday, push-ups!]] -;; Remind me to exercise every 1.5hours; starting at 8:00am. -(my/notify \"Take a 5min break and get your blood flowing!\" - :titled \"Exercise\" - :at \"8:00am\" - :repeat-every-hour 1.5) +* Life within Org-mode :Posterity: +:PROPERTIES: +:CUSTOM_ID: Life-within-Org-mode +:header-args: :tangle yes +:END: -;; Actually getting things done! -(my/notify \"Is what you're doing actually in alignment with your goals? - Maybe it's time to do another task?\" - :titled \"Check your agenda!\" - :at \"10:00am\" - :repeat-every-hour 2) -" - (run-at-time at ;; the time to make the alert - (when repeat-every-hour (* 60 60 repeat-every-hour)) - #'async-shell-command - (format "%s" - `(terminal-notifier - -title ,(pp-to-string titled) - -message ,(s-replace "\\n" "\n" (pp-to-string message)) - ;; Play a random sound when the notification appears. See sound names with: ls /System/Library/Sounds - ;; Use the special NAME “default” for the default notification sound. - -sound ,(progn (require 'seq) (seq-random-elt (s-split "\n" (shell-command-to-string "ls /System/Library/Sounds")))) - ;; Don't create duplicates of the notification, just one instance; - ;; i.e., each notification belongs to a group and only one alert of the group may be present at any one time. - -group ,(pp-to-string titled) - ;; Activate the application specified by ID when the user clicks the notification. - -activate org.gnu.Emacs - ,@(when open `(-open ,(pp-to-string open))) - ;; Run the shell command COMMAND when the user clicks the notification. - ;; -execute COMMAND - & ;; … and then speak! … - ;; NOTE:This was getting annyoning in the middle of work meetings. - ;; say ,(s-replace "\\n" " " (pp-to-string message)) - )))) -#+end_src +It's hard to estimate how long a task takes if you don't keep track of time +spent by ‘clocking-in and clocking-out’ of tasks. We can ‘capture’ todos right +in the middle of a task /without/ context-switching; e.g., no opening a todos +file! After some reflection on the relative importance of the tasks, we can +schedule them into our ‘agenda’. -The following two actual uses cases are also mentioned in doc:my/notify -docstring, since I want the documentation to be self-contained. -#+begin_src emacs-lisp -;; (Emojis look terrible in Lisp; but much better when the alert is actually made!) + Let's do this! -;; Remind me to exercise every 1.5hours; starting at 8:00am. -(my/notify "Take a 5min break and get your blood flowing!\n\t\t🚣 🏃‍♂️ 🧗‍♂️ 🧘‍♂️ 🏊 🏋 🚴‍♂️" - :titled "🤾‍♀️ Exercise 🚵‍♂️" - :at "8:00am" - :repeat-every-hour 1.5 - :open "https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity") +** Using Org-Mode as a Day Planner +:PROPERTIES: +:CUSTOM_ID: Using-Org-Mode-as-a-Day-Planner +:END: -;; Actually getting things done! -(my/notify "Is what you're doing actually in alignment with your goals? ✔️📉 - Maybe it's time to do another task? 📋" - :titled "📆 Check your agenda! 🔔" - :at "10:00am" - :repeat-every-hour 2) -#+end_src + ⟪ This section is based on a dated, yet delightful, tutorial + of the same title by [[http://newartisans.com/2007/08/using-org-mode-as-a-day-planner/][John Wiegley]]. ⟫ -+ [[https://emacs.stackexchange.com/questions/3844/good-methods-for-setting-up-alarms-audio-visual-triggered-by-org-mode-events][Here]] is an approach to triggering audio-visual alarms from Org-mode events - ---using ~org-agenda-to-appt~. +We want a day-planner with the following use: +0. “Mindlessly” & rapidly create new tasks. +1. Schedule and archive tasks at the end, or start, of the work day. +2. Glance at a week's tasks, shuffle if need be. +3. Prioritise the day's tasks. Aim for ≤15 tasks. +4. Progress towards completion of ~A~ tasks by documenting work completed. +5. Repeat! During the day, if anything comes up, capture it and intentionally + forget about it. -+ Emacs's built in [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Appointments.html][appointment notification facility]] can also be used as a alarm - clock via ~M-x appt-add~. +#+begin_box :background-color orange +#+begin_center +badge:Workflow|CheatSheet|informational|https://alhassy.github.io/emacs.d/Workflow.pdf -The [[https://github.com/jcs-elpa/marquee-header][marquee-header]] package let's show messages as “horizontal moving text along -the top of the Emacs frame”, which is neat. -- Slightly related is [[https://github.com/jcs-elpa/logms][logms]], which let's us make ~message~ calls but we can also - /see/ the context/source where those calls were made; as well as a clickable - link back to the source. +A 1-page, 3-column, PDF summarising the ideas and keybindings of this section +~(งಠ_ಠ)ง~ +#+end_center +#+end_box -** COMMENT Using Gnus for Gmail [Temporarily Disabled] +*** Capturing ideas & notes without interrupting the current workflow :Disabled: :PROPERTIES: -:CUSTOM_ID: Using-Gnus-for-Gmail +:CUSTOM_ID: Capturing-ideas-notes-without-interrupting-the-current-workflow +:header-args: :tangle no :END: -:Alread_done_elsewhere: -Let's set the following personal Emacs-wide variables ---to be used in other -locations besides email. +[[https://orgmode.org/org.html#Setting-up-capture][Capture]] lets me quickly make notes & capture ideas, with associated reference +material, without any interruption to the current work flow. Without losing +focus on what you're doing, quickly jot down a note of something important that +just came up. +#+begin_details ‘my/org-capture’ Implementation #+begin_src emacs-lisp -(setq user-full-name "Musa Al-hassy" - user-mail-address "alhassy@gmail.com") -#+end_src - -For some fun, run this cute method. -#+BEGIN_SRC emacs-lisp :tangle no -(animate-birthday-present user-full-name) -#+END_SRC -:End: +(cl-defun my/org-capture-buffer (&optional keys no-additional-remarks + (heading-regexp "Subject: \\(.*\\)")) + "Capture the current [narrowed] buffer as a todo/note. -By default, in Emacs, we may send mail: Write it in Emacs with ~C-x m~ ---or -doc:compose-mail---, then press ~C-c C-c~ to have it sent via your OS's default -mailing system ---mine appears to be Gmail via the browser. Or cancel sending -mail with ~C-c C-k~ ---the same commands for org-capturing, discussed earlier -(•̀ᴗ•́)و +This is mostly intended for capturing mail as todo tasks ^_^ -Folowing [[https://eschulte.github.io/emacs24-starter-kit/starter-kit-gnus.html][this tutorial]], to send and read email in Emacs we use [[https://en.wikipedia.org/wiki/Gnus][GNUS]], which, like -GNU itself, is a recursive acronym: GNUS Network User Service. +When NO-ADDITIONAL-REMARKS is provided, and a heading is found, +then make and store the note without showing a pop-up. +This is useful for when we capture self-contained mail. - 1. Execute, rather place in your init: - #+begin_src emacs-lisp - (setq message-send-mail-function 'smtpmail-send-it) - #+end_src - Revert to the default OS mailing method by setting this variable to - ~mailclient-send-it~. +The HEADING-REGEXP must have a regexp parenthesis construction +which is used to obtain a suitable heading for the resulting todo/note." + (interactive "P") + (let* ((current-content (substring-no-properties (buffer-string))) + (heading (progn (string-match heading-regexp current-content) + (or (match-string 1 current-content) "")))) + (org-capture keys) + (insert heading "\n\n\n\n" (s-repeat 80 "-") "\n\n\n" current-content) - # (gnutls-available-p) + ;; The overtly verbose conditions are for the sake of clarity. + ;; Moreover, even though the final could have “t”, being explicit + ;; communicates exactly the necessary conditions. + ;; Being so verbose leads to mutual exclusive clauses, whence order is irrelevant. + (cond + ((s-blank? heading) + (beginning-of-buffer) (end-of-line)) + ((and no-additional-remarks (not (s-blank? heading))) + (org-capture-finalize)) + ((not (or no-additional-remarks (s-blank? heading))) + (beginning-of-buffer) (forward-line 2) (indent-for-tab-command))))) +#+end_src +With that in-hand, we use a wrapper to ~org-capture~ to make use of it. +#+begin_src emacs-lisp +(defun my/org-capture (&optional prefix keys) + "Capture something! - 2. Follow only the [[https://www.emacswiki.org/emacs/GnusGmail#toc1][quickstart here]]; namely, make a file named ~~/.gnus~ containing: - #+begin_src emacs-lisp :tangle ~/.gnus -;; user-full-name and user-mail-address should be defined + C-c c ⇒ Capture something +C-u C-c c ⇒ Capture current [narrowed] buffer. +C-u 5 C-c c ⇒ Capture current [narrowed] buffer without adding additional remarks. +C-u C-u C-c c ⇒ Goto last note stored. -;; Get mail using port 993/IMAP/“Internet Message Access Protocol” -(setq gnus-select-method - '(nnimap "gmail" - (nnimap-address "imap.gmail.com") - (nnimap-server-port 993) - (nnimap-stream ssl))) +At work, ‘C-c c’ just captures notes under ‘Tasks’; no menu used." + (interactive "p") + (pcase prefix + (4 (my/org-capture-buffer keys)) + (5 (my/org-capture-buffer keys :no-additional-remarks)) + (t (if my/personal-machine? + (org-capture prefix keys) + (org-capture prefix "t"))))) +#+end_src +#+end_details -;; Send mail using port 587/SMTP/“Simple Mail Transfer Protocol” -(setq message-send-mail-function 'smtpmail-send-it - smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil)) - smtpmail-auth-credentials '(("smtp.gmail.com" 587 "alhassy@gmail.com" nil)) - smtpmail-default-smtp-server "smtp.gmail.com" - smtpmail-smtp-server "smtp.gmail.com" - smtpmail-smtp-service 587) - #+end_src - # (system-packages-install "starttls") - # (setq gnus-ignored-newsgroups "^to\.\|^[0-9. ]+\( \|$\)\|^["]"[#'()]") +#+begin_src emacs-lisp :results raw replace :exports results :wrap box +(s-join "\n" (--map (concat "+ [[kbd:" (s-replace "⇒" "]]" it)) (cddr (s-split "\n" (documentation #'my/org-capture))))) +#+end_src - 3. Get an email password for GNUS: - 1. Go to https://myaccount.google.com/security. - 2. Enable ~2-Step Verification~ - 3. Click on ~App passwords~, login, then generate a new password - with, say, name ~Emacs Gnus~. - 4. You will then obtain a secret password, the ~x~ marks below, which you insert - in a file named ~~/.authinfo~ as follows ---using your email address. - #+begin_src shell :tangle no - ​machine smtp.gmail.com login alhassy@gmail.com password xxxxxxxxxxxxxxxx port 587 - ​machine imap.gmail.com login alhassy@gmail.com password xxxxxxxxxxxxxxxx port 993 - default login alhassy@gmail.com password xxxxxxxxxxxxxxxx - #+end_src - - 4. In Emacs, ~M-x gnus~ to see what's there. +#+RESULTS: +#+begin_box ++ [[kbd: C-c c ]] Capture something ++ [[kbd:C-u C-c c ]] Capture current [narrowed] buffer. ++ [[kbd:C-u 5 C-c c ]] Capture current [narrowed] buffer without adding additional remarks. ++ [[kbd:C-u C-u C-c c ]] Goto last note stored. +#+end_box - - Or compose mail with ~C-x m~ then send it with ~C-c C-c~. - - Press ~C-h m~ to learn more about message mode for mail composition; or - read the [[https://www.gnus.org/manual/message.pdf][Message Manual]]. +E.g., I have a task, or something I wish to note down, rather than opening some +file, then making a heading, then writing it; instead, I press kbd:C-c_c_t and a +pop-up appears, I make my note, and it disappears ---with my notes file(s) now +being altered! Moreover, by default it provides a timestamp and a link to the +file location where I made the note ---helpful for tasks, tickets, to be tackled +later on. - Only news groups with /unread mail/ are shown; to see all your groups (Gmail - ‘tags’), press ~A A~ (doc:gnus-group-list-active), then press ~u~ to toggle - (un)subscription to such groups and they will show up in the main group - buffer ---if they have /unread mail/. See [[https://sachachua.com/blog/2008/05/emacs-gnus-organize-your-mail/][here]] for a tutorial on splitting - mail groups, automatically or fancily filing them away. +#+begin_src emacs-lisp +;; Location of my todos / captured notes file +(unless noninteractive + (setq org-default-notes-file + (if my/personal-machine? + "~/Dropbox/todo.org" + "~/Desktop/Work-2022-01-01.org"))) --------------------------------------------------------------------------------- +;; “C-c c” to quickly capture a task/note +(define-key global-map "\C-cc" #'my/org-capture) ;; See above. +#+end_src -#+BEGIN_SRC emacs-lisp -;; After startup, if Emacs is idle for 10 seconds, then start Gnus. -;; Gnus is slow upon startup since it fetches all mails upon startup. -(when my/personal-machine? - (run-with-idle-timer 10 nil #'gnus)) -#+END_SRC +#+begin_details "By default we only get a ‘tasks’ form of capture, let's add some more." +#+begin_src emacs-lisp +(cl-defun my/make-org-capture-template + (shortcut heading &optional (no-todo nil) (description heading) (scheduled nil)) + "Quickly produce an org-capture-template. -Learn more by reading [[https://www.gnu.org/software/emacs/manual/html_mono/gnus.html#Top][The Gnus Newsreader Manual]]; also available within Emacs by -~C-h i m gnus~ (•̀ᴗ•́)و + After adding the result of this function to ‘org-capture-templates’, + we will be able perform a capture with “C-c c ‘shortcut’” + which will have description ‘description’. + It will be added to the tasks file under heading ‘heading’. -- Or look at the [[https://www.gnu.org/software/emacs/refcards/pdf/gnus-refcard.pdf][Gnus Reference Card]]. -- Or, less comprehensively, this [[https://github.com/redguardtoo/mastering-emacs-in-one-year-guide/blob/master/gnus-guide-en.org#subscribe-groups][outline]]. -- [[https://www.emacswiki.org/emacs/GnusTutorial][EmacsWiki]] has a less technical and more user friendly tutorial. -- Other possibly useful links: - + [[http://www.cataclysmicmutation.com/2010/11/multiple-gmail-accounts-in-gnus/][Multiple Gmail accounts in Gnus]] + ‘no-todo’ omits the ‘TODO’ tag from the resulting item; e.g., + when it's merely an interesting note that needn't be acted upon. --------------------------------------------------------------------------------- + Default for ‘description’ is ‘heading’. Default for ‘no-todo’ is ‘nil’. -#+begin_details Super Terse Tutorial + Scheduled items appear in the agenda; true by default. -link-here:Super-Terse-Tutorial + The target is ‘file+headline’ and the type is ‘entry’; to see + other possibilities invoke: C-h o RET org-capture-templates. + The “%?” indicates the location of the Cursor, in the template, + when forming the entry. + " + `(,shortcut ,description entry + (file+headline org-default-notes-file ,heading) + ,(concat "*" (unless no-todo " TODO") " %?\n" + (when nil ;; this turned out to be a teribble idea. + ":PROPERTIES:\n:" + (if scheduled + "SCHEDULED: %^{Any time ≈ no time! Please schedule this task!}t" + "CREATED: %U") + "\n:END:") "\n\n ") + :empty-lines 1 :time-prompt t)) +#+end_src +#+begin_src emacs-lisp +(setq org-capture-templates + (cl-loop for (shortcut heading) + in (-partition 2 '("t" "Tasks, Getting Things Done" + "r" "Reference Material" + "m" "Email" + "e" "Emacs (•̀ᴗ•́)و" + "i" "Islam" + "b" "Blog" + "a" "Arbitrary Reading and Learning" + "l" "Programming Languages" + "p" "Personal Matters")) + collect (my/make-org-capture-template shortcut heading))) +#+end_src - ⟨ See the [[https://www.gnu.org/software/emacs/refcards/pdf/gnus-refcard.pdf][GNUS Reference Card]]! ⟩ +Rather than adding notes to particular Org headings in my =todo.org= file, I could +defer such a choice by having only one template and have ~C-c a~ automatically use +it. Then I could ‘refile’ tasks to their appropriate parent headings with ~w~. +This allows us to seperate the concerns of capturing ideas from doing any form +of processing. Something to consider. -In gnus, by default items you've looked at disappear ---i.e., are archived. -They can still be viewed in, say, your online browser if you like. -In the ~Group~ view, ~R~ resets gnus, possibly retriving mail or alterations -from other mail clients. ~q~ exits gnus in ~Group~ mode, ~q~ exits the particular -view to go back to summary mode. Only after pressing ~q~ from within a group -do changes take effect on articles ---such as moves, reads, deletes, etc. +#+begin_src emacs-lisp +;; Update: Let's schedule tasks during the GTD processing phase. +;; +;; For now, let's automatically schedule items a week in advance. +;; TODO: FIXME: This overwrites any scheduling I may have performed. +;; (defun my/org-capture-schedule () +;; (org-schedule nil "+7d")) +;; +;; (add-hook 'org-capture-before-finalize-hook 'my/org-capture-schedule) +#+end_src +#+end_details -+ Expected keys: ~RET~ enter/open an item, ~q~ quit and return to previous view, ~g~ - refresh view ---i.e., ‘g’et new articles. +For now I capture everything into a single file. One would ideally keep +separate client, project, information in its own org file. ++ ⇒ Org capture actually lets us add /any/ type of entry, ‘programmable template’, + to /any/ type of file! ⇐ + * Look at doc:my/make-org-capture-template, above, to notice that capture + actually lets you add /any/ type of item to /any/ file. ++ ( For now, I'm only using it to add entries to my tasks lists. ) ++ Org-protocol is a way to create capture notes in org-mode from other applications. -+ =RET=: Enter a group by pressing, well, the enter key. - - Use ~SPC~ to open a group and automatically one first article there. - - Use ~C-u RET~ to see all mail in a folder instead of just unread mail. +Let's also ensure *[[orange:TODO]]*-s respect hierarchical structure. +#+BEGIN_SRC emacs-lisp +;; Cannot mark an item DONE if it has a TODO child. +;; Conversely, all children must be DONE in-order for a parent to be DONE. +(setq org-enforce-todo-dependencies t) +#+END_SRC -+ Only groups/folders with unread mail will be shown, use ~L/l~ to toggle between - listing all groups. +*Where am I currently capturing?* ++ During meetings, when a nifty idea pops into my mind, I quickly capture it. + - I've found taking my laptop to meetings makes me an active listener + and I get much more out of my meetings since I'm taking notes. ++ Through out the day, as I browse the web, read, and work; random ideas pop-up, and I capture them indiscriminately. ++ I envision that for a phone call, I would open up a capture to make note of what the call entailed so I can review it later. ++ Yet another place to capture content is from mail, such as for reference + material, or self-contained tasks. + # See [[#Capturing-Mail-as-Todo-Notes][above]] for this discussion. ++ Anywhere you simply want to make a note, for the current heading, just press + kbd:C-c_C-z. The notes are just your remarks along with a timestamp; they are + collected at the top of the tree, under the heading. -+ ~SPC, DEL~ to scroll forward and backward; or ~C-v, M-v~ as always. + #+begin_src emacs-lisp + ;; Ensure notes are stored at the top of a tree. + (setq org-reverse-note-order nil) + #+end_src -+ =G G=: Search mail at server side in the group buffer. - - Limit search to particular folders/groups by marking them with ~#~, or - unmarking them with ~M-#~. +Quickly look up some reference material... +#+begin_src emacs-lisp +(cl-defun my/reference (&optional (file org-default-notes-file)) + "Look up some reference material super quick. -+ ~/ /,a:~ Filter mail according to subject or author; there are many - other options, see [[https://www.gnu.org/software/emacs/manual/html_mono/gnus.html#Limiting][§3.8 Limiting]]. +By default we look in user's Org TODOs file. -+ =d=: Mark an article as done, i.e., read it and it can be archived. +FILE should be an ORG file with a top-level heading that starts with ‘Reference’. +We show its subheadings in a completing-read menu, then narrow to that entry." + (interactive) + (find-file file) + (widen) + (goto-char (point-min)) + (re-search-forward "^* Reference") ;; Start of line. + (org-narrow-to-subtree) + (org-cycle) (org-cycle) + (let* ((headings (org-map-entries (lambda () (org-element-property :title (org-element-at-point)) ) "LEVEL=2")) + (topic (completing-read "What to review? " headings))) + (search-forward (concat "** " topic)) + (org-narrow-to-subtree) + (org-cycle))) -+ =!=: Mark an article as read, but to be kept around ---e.g., you have not - replied to it, or it requires more reading at a later time. +(defalias 'my/review-reference-notes 'my/reference) +(defalias 'w-reference 'my/reference) ;; “w”ork +#+end_src - This lets us read mail offline; cached mail is found at =~/News/cache/=. +**** COMMENT Emacs as a reference platform: Instead of manually finding files, looking for :Place_beside_w_reference_function: +:PROPERTIES: +:CUSTOM_ID: COMMENT-Emacs-as-a-reference-platform-Instead-of-manually-finding-files-looking-for +:END: + info, then closing them; we can press a key combination to see some reference + matter _quickly_ then press _q_ to dismiss it just as quickly. We can do this with + a combination of (local-set-key (kbd "q") 'kill-buffer-and-window) and + (read-only-mode); but better would be to open a file and put it in special-mode + which has this _q_ behaviour [see simple.el special-mode-map for other bindinfs] + or to use the built-in with-electric-help macro, or make-help-screen & (require + 'help-macro).. Moreover, for lots of reference matter, a hydra can be used to + obtain a nice menu. My setup is to use doc:w-reference. - #+BEGIN_SRC emacs-lisp :tangle "~/.gnus" - (setq gnus-use-cache 'use-as-much-cache-as-possible) -#+END_SRC -+ =B m=: Move an article, in its current state, to another group ---i.e., - ‘label’ using Gmail parlance. +Anyhow… +*** Step 1: When new tasks come up +:PROPERTIES: +:CUSTOM_ID: Step-1-When-new-tasks-come-up +:END: - - Something to consider doing when finished with an article. +Isn't it great that we can squirrel away info into some default location +then immediately return to what we were doing before ---with speed & minimal distraction! ♥‿♥ +Indeed, if our system for task management were slow then we may not produce +tasks and so forget them altogether! ~щ(゜ロ゜щ)~ - To delete an article, simply move it to ‘trash’ ---of course this will delete it - in other mail clients as well. There is no return from trash. ++ Entering tasks is a desirably impulsive act; do not make any further + scheduling considerations. - Emails can always be archieved ---never delete, maybe? + The next step, the review stage occurring at the end or the start of the + workday, is for processing. - Anyhow, ~B m Trash~ is too verbose, let's just use ~t~ for “trash”: - #+BEGIN_SRC emacs-lisp -(with-eval-after-load 'gnus - (bind-key "t" - (lambda (N) (interactive "P") (gnus-summary-move-article N "[Gmail]/Trash")) - gnus-summary-mode-map)) +#+begin_quote +The reason for this is that entering new tasks should be impulsive, not reasoned. +Your reasoning skills are required for the task at hand, not every new tidbit. +You may even find that during the few hours that transpire between creating a +task and categorizing it, you’ve either already done it or discovered it doesn’t +need to be done at all! ---[[http://newartisans.com/2007/08/using-org-mode-as-a-day-planner/][John Wiegley]] +#+end_quote -;; Orginally: t ⇒ gnus-summary-toggle-header -#+END_SRC +When my computer isn't handy, I'll make a note on my phone then transfer it later. +*** Step 2: Filing your tasks +:PROPERTIES: +:CUSTOM_ID: Step-2-Filing-your-tasks +:END: +At a later time, a time of reflection, we go to our tasks list and actually +schedule time to get them done by kbd:C-c_C-s, doc:org-schedule, then pick a +date by entering a number in the form ~+𝓃~ to mean that task is due ~𝓃~ days from +now. - - Select and deselect many articles before - moving them by pressing ~#~ and ~M-#~, respectively, anywhere on the entry. - - - As usual, you can mark a region, =C-SPC=, then move all entries therein. - -+ =R, r=: Reply with sender's quoted text in place, or without but - still visible in an adjacent buffer. - - Likewise ~S W~ or ~S w~ to reply all, ‘wide reply’, with or without quoted text. - - ~C-c C-z~ Delete everything from current position till the end. - - ~C-c C-e~ Replace selected region with ‘[...]’; when omitting parts of quoted text. - -+ Press ~m~ to compose mail; or ~C-x m~ from anywhere in Emacs to do so. - - ~C-c C-c~ to send the mail. - - ~S D e~ to resend an article as new mail: Alter body, subject, etc, before - - ~C-c C-f~ to forward mail. - sending. ++ Tasks with no due date are ones that “could happen anytime”, most likely no time at all. ++ At least schedule tasks reasonably far off in the future, then reassess when the time comes. ++ An uncompleted task is by default rescheduled to the current day, each day, along with how overdue it is. + - Aim to consciously reschedule such tasks! -+ ~C-c C-a~ to attach a file; it'll be embedded in the mail body as plaintext. - - Press ~o~ on an attachment to save it locally. -#+end_details + Let's keep track of how many times, and when, we have pushed events to other dates. + #+BEGIN_SRC emacs-lisp +;; Add a note whenever a task's deadline or scheduled date is changed. +(setq org-log-redeadline 'time) +(setq org-log-reschedule 'time) +#+END_SRC -#+begin_details GNUS Prettifications +#+begin_box custard + With time, it will become clear what is an unreasonable day + verses what is an achievable day. +#+end_box -link-here:gnus-prettifications +[[https://orgmode.org/manual/Repeated-tasks.html][Repeat tasks]] by a repeater such as ‘+1m’ or ‘+7d’ in their timestamps; e.g., +~:SCHEDULED: <2005-10-01 Sat +1m>.~ Likewise, to schedule an event (to repeat) for +multiple days, just use a bunch of timestamps (with repeaters): ~:SCHEDULED: +<2022-01-04 Tues 9:00 +1w><2022-01-06 Thu +1w><2022-01-07 Fri 9:00 +1w>~. +(Notice that we have ~:~ on each side of the keyword; and we have a time of day +for the event.) -Let's add the icon  near my mail groups ^_^ -#+BEGIN_SRC emacs-lisp -;; Fancy icons for Emacs -;; Only do this once: -(use-package all-the-icons - :config (all-the-icons-install-fonts 'install-without-asking)) +-------------------------------------------------------------------------------- -;; Make mail look pretty -(use-package all-the-icons-gnus - :defer t - :config (all-the-icons-gnus-setup)) +A ‘project’ is a task that has multiple steps, each as a checkbox item. It can +be given a percentage marker to show progress: Place ~[%]~ after its name, then +press [[kbd:C-c_#]] ---doc:org-update-statistics-cookies--- on the name to see a +completion percentage ---press kbd:C-c_C-c on a checkbox item to toggle its +completion state. -;; While we're at it: Make dired, ‘dir’ectory ‘ed’itor, look pretty -(use-package all-the-icons-dired - :hook (dired-mode . all-the-icons-dired-mode)) -#+END_SRC +*** Step 3: Quickly review the upcoming week +:PROPERTIES: +:CUSTOM_ID: Step-3-Quickly-review-the-upcoming-week +:END: -Next, let's paste in some [[http://groups.google.com/group/gnu.emacs.gnus/browse_thread/thread/a673a74356e7141f][eye-candy for Gnus]]: +The next day we begin our work, we press kbd:C-c_a_a to see the scheduled tasks +for this week ---kbd:C-c_C-s to re-schedule the task under the cursor and [[kbd:r]] +to refresh the agenda. #+begin_src emacs-lisp -(setq gnus-sum-thread-tree-vertical "│" - gnus-sum-thread-tree-leaf-with-other "├─► " - gnus-sum-thread-tree-single-leaf "╰─► " - gnus-summary-line-format - (concat - "%0{%U%R%z%}" - "%3{│%}" "%1{%d%}" "%3{│%}" - " " - "%4{%-20,20f%}" - " " - "%3{│%}" - " " - "%1{%B%}" - "%s\n")) +(define-key global-map "\C-ca" 'org-agenda) #+end_src -#+end_details -#+begin_details "Sending Mail with Lisp ---e.g., as a Bulk Mailer" -link-here:bulk-mailer -#+begin_src emacs-lisp :results replace :wrap template -(defun my/email (to subject body) - (compose-mail to subject) - (insert body) - (message-send-mail) ;; Appends info to the message buffer - ; (let ((kill-buffer-query-functions nil)) (kill-this-buffer)) - (ignore-errors (undo)) ;; Undo that addition - (message-kill-buffer) - (message "Send email to %s" to)) ;; Close that message buffer -#+end_src ++ Show the next 𝓃 days schedule ⇐ =C-u 𝓃 C-c a a=. -#+begin_src emacs-lisp :results replace :wrap template :tangle no -;; Example -(my/email (format "%s <%s>" user-full-name user-mail-address) ;; To - "Test" ;; Subject - "Why hello there!") ;; Email body -#+end_src -#+end_details +# The next section, [[Super Agenda]], will discuss acting on entries in the agenda +# buffer. -#+begin_details [Disabled] Auto-completing mail addresses +-------------------------------------------------------------------------------- -# :CUSTOM_ID: Auto-completing-mail-addresses +Org agenda is an interactive tool for generating summary reports from Org data +---e.g., commonly, the weekly task list is generated from todo tasks. -In order to get going quickly, using [[https://github.com/redguardtoo/gmail2bbdb][gmail2bbdb]], let's convert our Gmail -contacts into a BBDB file ---the [[http://bbdb.sourceforge.net/][Insidious Big Brother Database]] is an -address-book application that we'll use for E-mail; if you want to use it as a -address-book application to keep track of contacts, notes, their organisation, -etc, then consider additionally installing [[https://github.com/emacs-helm/helm-bbdb][helm-bbdb]] which gives a nice menu -interface. +The agenda dispatch menu, ~C-c a~, has options for displaying tasks ---e.g., ~C-c a +m~ generates a list of entries having the same tags. new ways to view tasks by +altering the ~org-agenda-custom-commands~ variable ---e.g., above we added two, +one for completed tasks and one for unscheduled tasks. - - From the [[https://www.google.com/contacts][Gmail Contacts page]], obtain a =contacts.vcf= file by clicking “More -> - Export -> vCard format -> Export”. -- Run command =M-x gmail2bbdb-import-file= and select =contacts.vcf=; a ~bbdb~ file - will be created in my Dropbox folder. -- Press ~C-x m~ then begin typing a contact's name and you'll be queried about - setting up BBDB, say yes. +# Moreover, we can overlay due dates and priorities to tasks in a non-intrusive +# way that is easy to edit by hand. +#+begin_details "Let's setup the basics of our agenda." +#+begin_src emacs-lisp +;; List of all the files & directories where todo items can be found. Only one +;; for now: My default notes file. +(setq org-agenda-files (list org-default-notes-file)) -#+begin_src emacs-lisp :tangle no -(use-package gmail2bbdb - :defer t - :custom (gmail2bbdb-bbdb-file "~/Dropbox/bbdb")) +;; Display tags really close to their tasks. +(setq org-agenda-tags-column -10) -(use-package bbdb - :after company ;; The “com”plete “any”thig mode is set below in §Prose - :hook (message-mode . bbdb-insinuate-gnus) - (gnus-startup-hook . bbdb-insinuate-gnus) - :custom (bbdb-file gmail2bbdb-bbdb-file) - (bbdb-use-pop-up t) ;; allow popups for addresses - :config (add-to-list 'company-backends 'company-bbdb)) - #+end_src +;; How many days ahead the default agenda view should look +(setq org-agenda-span 'day) +;; May be any number; the larger the slower it takes to generate the view. +;; One day is thus the fastest ^_^ -Here is an [[http://emacs-fu.blogspot.com/2009/08/managing-e-mail-addresses-with-bbdb.html][emacs-fu]] article on managing e-mail addressed with bbdb. -#+end_details +;; How many days early a deadline item will begin showing up in your agenda list. +(setq org-deadline-warning-days 14) -#+begin_details [Disabled] Feeds to Blogs -link-here:gnus-feeds-to-blogs -One can easily subscribe to an RSS feed in Gnus: Just press ~G R~ in the group -buffer view, then follow the prompts. However, doing so programmatically is much -harder. Below is my heartfelt attempt at doing so ---if you want a feed reader -in Emacs that “just works”, then [[https://github.com/skeeto/elfeed][elfeed]] is the way to go. When all is said and -done, the code below had me reading Gnus implementations and led me to conclude -that /Gnus has a great key-based interface but a /poor programming interface/ ---or -maybe I need to actually read the manual instead of frantically consulting -source code. +;; In the agenda view, days that have no associated tasks will still have a line showing the date. +(setq org-agenda-show-all-dates t) -My homemade hack to getting tagged feeds programmatically into Gnus. -#+begin_src emacs-lisp :tangle no -;; Always show Gnus items organised by topic. -(add-hook 'gnus-group-mode-hook 'gnus-topic-mode) +;; Scheduled items marked as complete will not show up in your agenda view. +(setq org-agenda-skip-scheduled-if-done t) +(setq org-agenda-skip-deadline-if-done t) +#+end_src +#+end_details -;; From Group view, press ^, then SPC on Gwene, then look for the site you want to follow. -;; If it's not there, add it via the web interface http://gwene.org/ -(add-to-list 'gnus-secondary-select-methods '(nntp "news.gwene.org")) -;; -;; E.g., http://nullprogram.com/feed/ uses an Atom feed which Gnus does not -;; support natively. But it can be found on Gwene. +#+begin_details Super Simple ‘agenda’ Mini-tutorial +link-here:Super-Simple-‘agenda’-Mini-tutorial -(setq my/gnus-feeds - ;; topic title url - '(Emacs "C‘est La 𝒵" https://cestlaz.github.io/rss.xml - Emacs "Marcin Borkowski's Blog" http://mbork.pl?action=rss - Emacs "Howardism" http://www.howardism.org/rss.xml - Islam "Shia Islam Blogspot" http://welcometoshiaislam.blogspot.com/feeds/posts/default?alt=rss - Cats "Hedonistic Learning" http://www.hedonisticlearning.com/rss.xml - Cats "Functorial Blog" https://blog.functorial.com/feed.rss - Programming "Joel on Software" http://www.joelonsoftware.com/rss.xml - Haskell "Lysxia's Blog" https://blog.poisson.chat/rss.xml)) +The agenda view, like nearly all Emacs entities, is interactive: ++ =𝓃 f,b= ⇒ Look forward at next week's agenda, or backward to a previous week. + - The optional $𝓃$ means do the action =𝓃=-many times; + it defaults to 1. ++ =w, d= ⇒ toggle week view, or day view; use ~v~ to see possible views. + - E.g., ~C-u 2017 v y~ shows us the specific year 2017. ++ =𝓃 n,p= to navigate to next and previous entries. ++ =t= ⇒ cycle TODO state of the current entry. ++ =±= ⇒ cycle priority state. ++ =𝓃 S-⇆= ⇒ Shift date time by $𝓃$ days; 1 day by default. ++ =C-c C-s= ⇒ Reschedule an entry; prefix it with ~C-u~ to remove a scheduled entry. + - [[https://orgmode.org/manual/Repeated-tasks.html][Repeat tasks]] by a repeater such as ‘+1m’ or ‘+7d’ + in their timestamps; e.g., ~DEADLINE: <2005-10-01 Sat +1m>.~ ++ =s= ⇒ save all agenda buffers; i.e., save the org-files where the agenda items live. ++ =g= ⇒ Rebuild agenda according to any changes made thus far. ++ =F= ⇒ Toggle ‘follow mode’: As you go up/down entries, you can see their + details in an adjacent window. + - =SPC= ⇒ Show details of a single entry in other window; stay in Agenda. ++ =RET, TAB= ⇒ Go to the current entry in the current window or in a new + adjacent window, so as to alter task details. -;; If fubared, then: -;; (ignore-errors (f-delete "~/News/" 'force) (f-delete "~/.newsrc.eld")) +# ;; Easy way to capture tasks when in agenda view. +# (define-key org-agenda-mode-map "c" #'org-capture) -;; Execute this after a Gnus buffer has been opened. -(progn -(use-package with-simulated-input) -(cl-loop for (topic title url) - in (-partition 3 my/gnus-feeds) - ;; url & topic are symbols, make them strings. - for url′ = (symbol-name url) - for topic′ = (symbol-name topic) - ;; Avoid spacing issues by using a Unicode ghost space “ ”. - for title′ = (gnus-newsgroup-savable-name (s-replace " " " " title)) - for input = (format "C-SPC C-a %s RET RET" title′) - do - ; cl-letf* (((symbol-function 'insert) (lambda (x) nil))) ;; see the (undo) below. - ;; Add the group - (with-simulated-input input - (gnus-group-make-rss-group url′)) - ;; Ensure it lives in the right topic category. - (if (equal 'no-such-topic (alist-get topic gnus-topic-alist 'no-such-topic nil #'string=)) - (push (list topic′ title′) gnus-topic-alist) ;; make topic if it doesnt exist - (setf (alist-get topic′ gnus-topic-alist 'no-such-topic nil #'string=) - (cons title′ (alist-get topic gnus-topic-alist 'no-such-topic nil #'string=))))) - ;; Acknowledgement - (message "Now switch into the GNUS group buffer, and refresh the topics; i.e., t t.")) +#+end_details - ;; The previous command performs an insert, since it's intended to be interactively - ;; used; let's undo the insert. - ; (undo-only) +The agenda view –--even in the 7-days-at-a-time view--– will always begin on the +current day. This is important, since while using org-mode as a day planner, +you never want to think of days gone past. That’s something you do in other +ways, such as when reviewing completed tasks. +#+begin_src emacs-lisp +(setq org-agenda-start-on-weekday nil) -;; (setq gnus-permanently-visible-groups ".*") -;; -;; Show topic alphabetically? The topics list is rendered in reverse order. -;; (reverse (cl-sort gnus-topic-alist 'string-lessp :key 'car)) +;; Start each agenda item with ‘○’, then show me it's %timestamp and how many +;; times it's been re-%scheduled. +(setq org-agenda-prefix-format " ○ %t%s") #+end_src -Ironically, I've decide that “no, I do not want to see my blogs in Emacs” for -the same reasons I do not activelly use ~M-x eww~ to browse the web in Emacs: I -like seeing the colours, fonts, and math symbols that the authours have labored -over to producing quality content. Apparently, I'm shallow and I'm okay with it ----but not that shallow, since I'm constantly pushing Emacs which looks ugly by -default but it's unreasonably powerful. -#+end_details -*** Capturing Mail as Todo/Notes -:PROPERTIES: -:CUSTOM_ID: Capturing-Mail-as-Todo-Notes -:END: +#+begin_details Grouping agenda entries together +Instead of having the day's tasks all in one field, org-super-agenda allows us +to use predicates to group entries together; e.g., by considering an entry's +=:tags:= or its priority level. Since I'm placing all my tasks in a single file, +under appropriate parent headings, I want entries to be shown according to their +parent heading. Of-course, the top-most grouping, the important tasks, should be +pulled out of their group and placed at the top. +#+BEGIN_SRC emacs-lisp +(use-package origami ) +(use-package org-super-agenda -Sometime mail contains useful reference material or may be a self-contained -task. Rather than using our inbox as a todo-list, we can copy the content of the -mail and store it away in our todos/notes files. [[#Capturing-ideas-notes-without-interrupting-the-current-workflow][Capturing]], above, is a way to, -well, capture ideas and notes /without/ interrupting the current workflow. Above, -in the section on capturing, we define doc:my/org-capture-buffer which quickly -captures the contents of the current buffer as notes to store away. We use that -method in the article view of mail so that [[kbd:c]] captures mail content with the -option to provide additional remarks, and [[kbd:C]] to silently do so without -additional remarks. + :hook (org-agenda-mode . origami-mode) ;; Easily fold groups via TAB. + :bind (:map org-super-agenda-header-map ("" . origami-toggle-node)) + :config + (org-super-agenda-mode) + (setq org-super-agenda-groups + '((:name "Important" :priority "A") + (:name "Personal" :habit t) + ;; For everything else, nicely display their heading hierarchy list. + (:auto-map (lambda (e) (org-format-outline-path (org-get-outline-path))))))) -#+BEGIN_SRC emacs-lisp -(with-eval-after-load 'gnus - ;; Orginally: c ⇒ gnus-summary-catchup-and-exit - (bind-key "c" #'my/org-capture-buffer gnus-article-mode-map) - ;; Orginally: C ⇒ gnus-summary-cancel-article - (bind-key "C" - (lambda (&optional keys) - (interactive "P") (my/org-capture-buffer keys 'no-additional-remarks)) - gnus-article-mode-map)) +;; MA: No noticable effect when using org-super-agenda :/ +;; +;; Leave new line at the end of an entry. +;; (setq org-blank-before-new-entry '((heading . t) (plain-list-item . t))) #+END_SRC -Gnus’ default =c= only enables a bad habit: Subscribing to stuff that you don't -read, since you can mark all entries as read with one key. We now replace it -with a ‘c’apturing mechanism that captures the current message as a todo or note -for further processing. Likewise, the default =C= is to cancel posting an article; -we replace it to be a /silent capture: Squirrel away informative mail content -without adding additional remarks./ -*** Email contacts +:OlderSetup: +#+BEGIN_SRC emacs-lisp :tangle no + ;; Default order is 0, first come first serve. + ;; Items are “or”-ed by default. + '((:name "Important" + :tag "PackageFormer" + :tag "PF" + :tag "research" + :and (:tag "JC" :priority "A") + :and (:tag "WK" :priority "A") + :priority "A") + + ;; Groups supply their own section names when none are given + (:name "Living the dream!" :tag "personal") + (:name "Discrete Mathematics" :tag "2fa3") + (:name "Emacs Init" :tag "init") + ;; (:priority<= "B" :order 1) + ;; Everything else automatically gets its own group + (:auto-category t) + ))) +#+END_SRC +:End: +# + Origami mode ---see below in [[Text Folding with Origami-mode]]--- +# works well with super-agenda. Just ~M-x origami-mode~ then ~C-c f~ to +# enable the folding hydra. +# +The [[https://github.com/alphapapa/org-super-agenda][org-super-agenda]] homepage shows complex configurations and pleasant +screenshots contrasting with and without the system. +E.g., you can change how entries in particular headings are displayed and coloured. +#+end_details +*** Step 4: Getting ready for the day :PROPERTIES: -:CUSTOM_ID: Email-contacts +:CUSTOM_ID: Step-4-Getting-ready-for-the-day :END: +After having seen our tasks for the week, we press [[kbd:d]] to enter daily view for +the current day. Now we decide whether the items for today are ~A~: of high +urgency & important; ~B~: of moderate urgency & importance; or ~C~: Pretty much +optional, or very quick or fun to do. ++ ~A~ tasks should be both important /and/ urgently done on the day they were scheduled. + - Such tasks should be relatively rare! + - If you have too many, you're anxious about priorities and rendering + priorities useless. ++ ~C~ tasks can always be scheduled for another day without much worry. + - Act! If the thought of rescheduling causes you to worry, upgrade it to a + ~B~ or ~A~. ++ As such, most tasks will generally be priority ~B~: + Tasks that need to be done, but the exact day isn't as critical as with an + ~A~ task. These are the “bread and butter” tasks that make up your day to day + life. -I have a personal file, ~contacts.org~, with Emacs Lisp src blocks contributing to -a list variable ~my/contacts~. This list consists of entries of the shape: -#+begin_src emacs-lisp :tangle no -(:name "Jasim Jasonsama" :phone 123-455-4321 :email bobert_billiam@emacsmail.com) +On a task item, or any org-heading, press [[kbd:,]] then one of [[kbd:A/B/C]] to set its +priority. Then [[kbd:r]] to refresh. + +#+begin_details Pretty Prioritisation Markers +link-here:Pretty-Prioritisation-Markers +Let's set four priority levels and their colours: The more intense colours +are for more urgent tasks. +#+begin_src emacs-lisp +(setq org-lowest-priority ?C) ;; Now org-speed-eky ‘,’ gives 3 options +(setq org-priority-faces +'((?A :foreground "red" :weight bold) ;; :background "LightCyan1") + (?B :foreground "orange" :weight bold) + (?C :foreground "green" :weight bold))) +;; See all colours with: M-x list-colors-display #+end_src -With the following snippet, I can write ~contacts~ then kbd:TAB to select a -personal contact. -#+begin_src org :noweb-ref templates-from-other-places-in-my-init :tangle no :comments none -,** contacts: Get the email of one of my personal contacts ++ ~C-c ,~ anywhere to set the priority of the current heading. + - We may press ~A-D~ or ~SPC~ to an remove existing priority. -${1:`(and (or (featurep 'my/contacts) (org-babel-load-file "~/Dropbox/contacts.org")) -(yas-choose-value (--map (format "%s <%s>" (getf it :name) (getf it :email)) -my/contacts)))`} $0 -#+end_src +Priority markers are of the form ~[#𝒳]~, the [[https://github.com/harrybournis/org-fancy-priorities][fancy priorities]] package visually +renders them as words or icons. +#+begin_src emacs-lisp +(use-package org-fancy-priorities + :hook (org-mode . org-fancy-priorities-mode) + :custom (org-fancy-priorities-list '("High" "MID" "LOW")) ;; "OPTIONAL" + ;; Let's use the “Eisenhower map of priority”… + ;; :custom (org-fancy-priorities-list '("Urgent and Important" ;; Do now! + ;; "Not Urgent But Important" ;; Do schedule this. + ;; "Urgent But Not Important" ;; Delegate? + ;; "Not Urgent and Not Important")) ;; Don't do / Optional + ) +#+end_src +#+end_details -** [[https://github.com/sabof/stripe-buffer][Add stripes to "list" buffers]] -:PROPERTIES: -:CUSTOM_ID: https-github-com-sabof-stripe-buffer-Add-stripes-to-list-buffers -:END: +At work, my tasks list is massive and constantly growing; so to avoid stressing +myself out, I try to add /time effort estimates/ to my tasks with +doc:org-set-effort then I open my Org agenda and run ~d~ (for day view) then +doc:org-agenda-columns (~C-c C-x C-c~) to see a column-view of my time estimates +/along/ with *a total time estimate for the day.* Since there are unexpected +pair-programming calls, or meetings go longer than expected, I only schedule for +7 hours each day (in a usual 8-hour work day); i.e., if my time estimates exceed +7h then I reschedule or cancel some things. #+begin_src emacs-lisp -;; Make every other line of a buffer grey (or whatever you like). -;; Useful for buffers that list things. -;; I want it to make my Org tables look nice. Even better when org-modern is activated. -(use-package stripe-buffer - :config (add-hook 'org-mode-hook 'turn-on-stripe-table-mode)) -#+end_src +(require 'org-agenda) -* TODO Cosmetics -:PROPERTIES: -:CUSTOM_ID: Cosmetics -:header-args: :tangle init.el -:END: +;; How should the columns view look? +(setq org-columns-default-format "%60ITEM(Task) %6Effort(Estim){:} %3PRIORITY %TAGS") -Upon startup, we want to be greeted with a useful, yet unobtrusive, message -briefly detailing major system details. Moreover, the bottom-most area of the -screen should display battery life, data, & time. Likewise, we may have a casual -file explorer ---primarily to show-off to newcomers, since great functionality -is found with ~M-x dired~ ---doc:dired. +;; Press “c” in Org agenda to see the columns view; (default binding C-c C-x C-c is too long!) +(org-defkey org-agenda-mode-map "c" #'org-agenda-columns) +(org-defkey org-agenda-mode-map "C" #'org-agenda-goto-calendar) -#+BEGIN_SRC emacs-lisp :tangle init.el -;; Get org-headers to look pretty! E.g., * → ⊙, ** ↦ ◯, *** ↦ ★ -;; https://github.com/emacsorphanage/org-bullets -(use-package org-bullets - :defer nil - :hook (org-mode . org-bullets-mode)) -#+END_SRC +;; Press “e” in columns view to alter “e”ffort “e”stimates. +(require 'org-colview) +(org-defkey org-columns-map "e" + ;; Refresh after making an effort estimate. + (lambda () (interactive) (org-agenda-set-effort) (org-agenda-columns))) +#+end_src -** Startup message: Emacs & Org versions +Near the top of my TODOS file, I have the following clickable link: It looks pretty and when I click it, takes me where I need to go. +# ;; Show sum of efforts for a day in Org-Agenda day title +#+begin_src org :tangle no +[[elisp:(progn (org-agenda nil "a") (org-agenda-columns) (delete-other-windows))][Show agenda with time estimates]] +#+end_src + +*** Step 5: Doing the work :PROPERTIES: -:CUSTOM_ID: Startup-message-Emacs-Org-versions +:CUSTOM_ID: Step-5-Doing-the-work :END: -Let's always welcome ourselves when Emacs begins with a helpful message. For -example, which user account is running and what are the version numbers of our -primary tools. +Since ~A~ tasks are the important and urgent ones, if you do all of the ~A~ tasks and +nothing else today, no one would suffer. It's a good day (─‿‿─). -#+begin_src emacs-lisp -;; Silence the usual message: Get more info using the about page via C-h C-a. -(setq inhibit-startup-message t) +There should be no scheduling nor prioritising at this stage. +You should not be touching your tasks file until your next review session: +Either at the end of the day or the start of the next. -(defun display-startup-echo-area-message () - "The message that is shown after ‘user-init-file’ is loaded." - (message - (concat "Welcome " user-full-name - "! Emacs " emacs-version - "; Org-mode " org-version - "; System " (symbol-name system-type) - "/" (system-name) - "; Time " (emacs-init-time)))) -#+end_src -Now my startup message is, -#+begin_example -Welcome Musa Al-hassy! Emacs 27.1; Org-mode 9.4.4; System darwin/Musas-MacBook-Air.local; Time 13.331914 seconds -#+end_example -:Manually_Computing_Init_Time: -#+BEGIN_SRC emacs-lisp :tangle no -(format "; Time %.3fs" - (float-time (time-subtract (current-time) before-init-time))) -#+END_SRC -:End: ++ Leverage priorities! E.g., When a full day has several ~C~ tasks, reschedule + them for later in the week without a second thought. + - You've already provided consideration when assigning priorities. -Let's change the Emacs frame to mention the name of the buffer in focus, -as well as a nice ‘motto’: -#+begin_src emacs-lisp -;; Keep self motivated! -(setq frame-title-format '("" "%b - Living The Dream (•̀ᴗ•́)و")) -#+end_src -** COMMENT My to-do list: The initial buffer when Emacs opens up +*** Step 6: Moving a task toward completion :PROPERTIES: -:CUSTOM_ID: My-to-do-list-The-initial-buffer-when-Emacs-opens-up +:CUSTOM_ID: Step-6-Moving-a-task-toward-completion :END: -I almost always have Emacs open; I don't need a dashboard, but would like to see -my to-do list and my init file, side-by-side. -#+BEGIN_SRC emacs-lisp -(unless noninteractive - ;; Only run the following when we're in GUI mode; - ;; i.e., don't run it in Github Actions when testing. - (if my/personal-machine? - (find-file "~/Dropbox/todo.org") - ;; After startup, if Emacs is idle for 10 seconds, then open my work file; - ;; which is a GPG file and so requires passphrase before other things can load. - ;; (run-with-idle-timer 10 nil (lambda () (find-file "~/Desktop/work.org.gpg"))) - (find-file "~/Desktop/Work-2022-01-01.org")) ;; Org-journal for work - (split-window-right) ;; C-x 3 - (other-window 1) ;; C-x 0 - (let ((enable-local-variables :all) ;; Load *all* locals. - (org-confirm-babel-evaluate nil)) ;; Eval *all* blocks. - (ignore-errors (find-file "~/.emacs.d/init.org")))) +My workflow states are described in the section +[[Workflow States]] and contain states: ~TODO, STARTED, WAITING, ON_HOLD, CANCELLED, DONE~. ++ Tasks marked ~WAITING~ are ones for which we are awaiting some event, like someone + to reply to our query. As such, these tasks can be rescheduled until I give up + or the awaited event happens ---in which case I go to ~STARTED~ and document + the reply to my query. ++ The task may be put off indefinitely with ~ON_HOLD~, or I may choose never to do it + with ~CANCELLED~. Along with ~DONE~, these three mark a task as completed + and so it needn't appear in any agenda view. -#+END_SRC +I personally clock-in and clock-out of tasks ---keep reading---, where upon +clocking-out I'm prompted for a note about what I've accomplished so far. +Entering a comment about what I've done, even if it's very little, feels like +I'm getting something done. It's an explicit marker of progress. -There is the neat-looking [[https://github.com/emacs-dashboard/emacs-dashboard][emacs-dashboard]] package that provides an extensbile -yet minimalist splash screen showing recent files, projects, and bookmarks. +In the past, I would make a “captain's log” at the end of the day, but that's +like commenting code after it's written, I didn't always feel like doing it and +it wasn't that important after the fact. The continuous approach of noting after +every clock-out is much more practical, for me at least. -** A sleek, informative, & fancy mode line +*** Step 7: Archiving Tasks :Disabled: :PROPERTIES: -:CUSTOM_ID: A-sleek-informative-and-fancy-mode-line +:CUSTOM_ID: Step-7-Archiving-Tasks +:header-args: :tangle no :END: +During the review state, when a task is completed, ‘archive’ it with +doc:org-archive-subtree or kbd:C-c_C-x_C-s: This marks it as done, adds a time +stamp, and moves it to a local ~*.org_archive~ file. What was our *[[red:‘to do’]]* list +becomes a *[[green:‘ta da’]]* list showcasing all we have done (•̀ᴗ•́)و -The ‘modeline’ is a part near the bottom of Emacs that gives information about -the current buffer, such as its file-type/‘major-mode’ and enabled -extensions/‘minor-modes’. Let's use the [[https://github.com/seagle0128/doom-modeline][doom-modeline]], which is a /sleek & -minimalistic, yet fancy/ setup with the following notable perks: +Archiving keeps task lists clutter free, but unlike deletion it allows us, +possibly rarely, to look up details of a task or what tasks were completed in a +certain time frame ---which may be a motivational act, to see that you have +actually completed more than you thought, provided you make and archive tasks +regularly. We can use [[doc:org-search-view][~M-x org-search-view~]] to search an org file /and/ the +archive file too, if we enable it so. +#+BEGIN_SRC emacs-lisp +;; C-c a s ➩ Search feature also looks into archived files. +;; Helpful when need to dig stuff up from the past. +(setq org-agenda-text-search-extra-files '(agenda-archives)) +#+END_SRC -+ Gives each buffer a nice icon in the modeline (denoting its major mode; e.g., - Lisp/JavaScript/Org/etc each get a cool icon). +#+begin_src emacs-lisp +;; enables the org-agenda variables. +(require 'org-agenda) ;; Need this to have “org-agenda-custom-commands” defined. -+ Name of file becomes red when unsaved/modified. +(unless noninteractive + ;; ➩ Show my agenda upon Emacs startup. + (org-agenda "a" "a")) +#+end_src -+ Nice version control icon, with branch name. +Let's install some helpful views for our agenda. -+ Name of file is of the shape is shown as “project/file.ext”, when a project - is detected using ~projectile.el~. ++ ~C-c a c~: See completed tasks at the end of the day and archive them. + #+begin_src emacs-lisp +;; Pressing ‘c’ in the org-agenda view shows all completed tasks, +;; which should be archived. +(add-to-list 'org-agenda-custom-commands + '("c" todo "DONE|ON_HOLD|CANCELLED" nil)) +#+end_src -+ Flycheck error reporting is ugly by default, and one would consider using - flycheck-status-emojis to make things look better in a simple modeline, but - Doom-modeline gives a nice status indicators for Flycheck. ++ ~C-c a u~: See unscheduled, undeadlined, and undated tasks in my todo files. + Which should then be scheduled or archived. + #+begin_src emacs-lisp +(add-to-list 'org-agenda-custom-commands + '("u" alltodo "" + ((org-agenda-skip-function + (lambda () + (org-agenda-skip-entry-if 'scheduled 'deadline 'regexp "\n]+>"))) + (org-agenda-overriding-header "Unscheduled TODO entries: ")))) +#+end_src -+ Shows “+2” when the text scale is two above usual. +/At the end of the day, let's schedule at least 3 things that must be done the +next day; i.e., have priority =A=./ -For fine-grained control on what/how things appear, there is -doc:doom-modeline-def-modeline and doc:doom-modeline-set-modeline. +** Tag! You're it! +:PROPERTIES: +:CUSTOM_ID: Tag-You're-it +:END: -#+begin_src emacs-lisp :tangle init.el -;; This package requires the fonts included with all-the-icons to be installed. Run M-x all-the-icons-install-fonts to do so. -;; The modeline looks really nice with doom-themes, e.g., doom-solarised-light. -(use-package doom-modeline - :defer nil - :config (doom-modeline-mode)) +Even when items are categorised under their own parent headings, they may be +related in some way and that can made explicit by adding a ~:tag:~ to their +headings; e.g., two entries both have the ~:jasim:@work:~ tags, then looking for +the ~:@work:~ tag shows me all entries that are tagged as “at work”. - ;; Use minimal height so icons still fit; modeline gets slightly larger when - ;; buffer is modified since the "save icon" shows up. Let's disable the icon. - ;; Let's also essentially disable the hud bar, a sort of progress-bar on where we are in the buffer. - (setq doom-modeline-height 1) - (setq doom-modeline-buffer-state-icon nil) - (setq doom-modeline-hud t) - (setq doom-modeline-bar-width 1) + | Tags provide a cross-section of one's entries. | - ;; Show 3 Flycheck numbers: “red-error / yellow-warning / green-info”, which - ;; we can click to see a listing. - ;; If not for doom-modeline, we'd need to use flycheck-status-emoji.el. - (setq doom-modeline-checker-simple-format nil) +Tags let us find related stuff quickly, even though they're differently +categorised. - ;; Don't display the buffer encoding, E.g., “UTF-8”. - (setq doom-modeline-buffer-encoding nil) +/After calling ~org-agenda~, we may select ~m~ to match for tags, or use +~org-tags-view~ to search for tags./ - ;; Inactive buffers' modeline is greyed out. - ;; (let ((it "Source Code Pro Light" )) - ;; (set-face-attribute 'mode-line nil :family it :height 100) - ;; (set-face-attribute 'mode-line-inactive nil :family it :height 100)) -#+end_src +*What to tag?* Common tags are =:@laptop:, :@work:, :@home:= to identify the +location where tasks take place ---Use: When I'm at a particular place, I need +only consider tasks that apply to that place ;-) Other tags I use are =:𝑭𝑳:= to +identify remarks or email or request from person 𝑭irstname 𝑳astname; or +something that might be interesting to that person. I also use ~:video:, :book:, +:paper:~; which let me quickly find all videos! Finally, I also use +=:project_name:= to identify notes that may be of interest to a particular +project, but are more appropriately categorised elsewhere ---e.g., when learning +about an Emacs feature, I may tag my notes with another project's name to +consider whether that feature could be useful there. -# I may not use the spacemacs [[https://www.emacswiki.org/emacs/StarterKits][starter kit]], since I find spacemacs to “hide things” -# from me ---whereas Emacs “encourages” me to learn more---, however it is a -# configuration and I enjoy reading Emacs configs in order to improve my own -# setup. From Spacemacs I've adopted Helm for list completion, its sleek light & -# dark themes, and its modified powerline setup. +*How to tag?* -:Disabled__spaceline_modeline: +You can just add a ~:tag₁:⋯:tagₙ:~ after a heading. If you press space, before the +tags, then they are automatically indented flushright to column 77; postive +numbers do not flushright but use exact column number. +#+BEGIN_SRC emacs-lisp + (setq org-tags-column -77) ;; the default +#+END_SRC -[[https://github.com/TheBB/spaceline][Spaceline]] +Use ~C-c C-q~, or ~org-set-tags-command~, on a heading or just the speed key ~:~ on +the asterisks of a heading to set the tags of an item ---as usual, with Helm we +obtain a window of all existing tags to select from. Unfortunatley, this only +supports having one tag; for more, you can add them in manually or … -#+begin_src emacs-lisp :tangle no -;; When using helm & info & default, mode line looks prettier. -(use-package spaceline - :custom (spaceline-buffer-encoding-abbrev-p nil) - ;; Use an arrow to seperate modeline information - (powerline-default-separator 'arrow) - ;; Show “line-number : column-number” in modeline. - (spaceline-line-column-p t) - ;; Use two colours to indicate whether a buffer is modified or not. - (spaceline-highlight-face-func 'spaceline-highlight-face-modified) - :config (custom-set-faces '(spaceline-unmodified ((t (:foreground "black" :background "gold"))))) - (custom-set-faces '(spaceline-modified ((t (:foreground "black" :background "cyan"))))) - (require 'spaceline-config) - (spaceline-helm-mode) - (spaceline-info-mode) - (spaceline-emacs-theme)) +#+begin_src emacs-lisp +;; Press ‘:’ on a heading to add a tag, press TAB to see all tags, press RETURN on a tag to add it, press TAB again to add more tags, when all done press RETURN twice. +(use-package helm-org ) ;; Helm for org headlines and keywords completion. +(add-to-list 'helm-completing-read-handlers-alist + '(org-set-tags-command . helm-org-completing-read-tags)) -(spaceline-toggle-buffer-size-off) ;; Not interested in how large a buffer is. -(spaceline-toggle-input-method-off) ;; Usually a “Π” symbol in the modeline, since I use Unicode input with Agda, denoting the current language environment, buffer coding system, and current input method. -(spaceline-toggle-buffer-encoding-abbrev-off) ;; buffer-encoding-abbrev: the line ending convention used in the current buffer (unix, dos or mac). -(spaceline-toggle-hud-off) ;; A tiny “progress bar”; shows the currently visible part of the buffer. -(spaceline-toggle-helm-buffer-id-off) ;; I don't need to see “Helm M-x” whenever I press “M-x” or other Helm-powered commands. +;; Also provides: helm-org-capture-templates #+end_src -Other separators ---of modeline information--- that I've considered include -~'brace~ instead of an arrow, and ~'contour, 'chamfer, 'wave, 'zigzag~ which look -like browser tabs that are curved, boxed, wavy, or in the style of driftwood. -:End: -*** Menu to Toggle Minor Modes: A quick way to see all of my modes, and which are enabled -:PROPERTIES: -:CUSTOM_ID: Menu-to-Toggle-Minor-Modes-A-quick-way-to-see-all-of-my-modes-and-which-are-enabled -:END: - -Enabled minor modes clutter up the modeline with their names, albeit some have -useful status information shown. We can either selectively pick which -names/status are shown using diminish.el, possibly forgetting which minor modes -are enabled or we can use minions.el to “gather up” all enabled minor modes, and -recently enabled ones, under a single menu which doom-modeline shows as a simple -configurations gear icon. ⚙. :gear: +Now ~:~ or ~C-c C-q~ will show existing tags for the current heading, press ~TAB~ to +obtain a list of all exisiting tags, press ~C-SPC~ to select the desired tags, +then =TAB= or =RET= to confirm the resulting tag list, and ~RET~ to finish or ~TAB~ to +select more tags. -#+begin_src emacs-lisp :tangle init.el - (setq doom-modeline-minor-modes t) - (use-package minions - :defer nil - :init (minions-mode)) +Let's render tags by Unicode symbols. +#+begin_src emacs-lisp +(use-package org-pretty-tags - ;; A quick hacky way to add stuff to doom-modeline is to add to the mode-line-process list. - ;; E.g.: (add-to-list 'mode-line-process '(:eval (format "%s" (count-words (point-min) (point-max))))) - ;; We likely want to add this locally, to hooks on major modes. + :config + (setq org-pretty-tags-surrogate-strings + '(("Neato" . "💡") + ("Blog" . "✍") + ("Audio" . "♬") + ("Video" . "📺") + ("Book" . "📚") + ("Running" . "🏃") + ("Question" . "❓") + ("Wife" . "💕") + ("Text" . "💬") ; 📨 📧 + ("Friends" . "👪") + ("Self" . "🍂") + ("Finances" . "💰") + ("Car" . "🚗") ; 🚙 🚗 🚘 + ("Urgent" . "🔥"))) ;; 📥 📤 📬 + (org-pretty-tags-global-mode 1)) #+end_src -*** Nice battery icon alongside with percentage, in doom-modeline + +** Automating [[https://en.wikipedia.org/wiki/Pomodoro_Technique][Pomodoro]] ---“Commit for only 25 minutes!” :PROPERTIES: -:CUSTOM_ID: Nice-battery-icon-alongside-with-percentage-in-doom-modeline +:CUSTOM_ID: Automating-https-en-wikipedia-org-wiki-Pomodoro-Technique-Pomodoro-Commit-for-only-25-minutes :END: -#+begin_src emacs-lisp :tangle init.el -;; If not for doom-modeline, we'd need to use fancy-battery-mode.el. -(display-battery-mode +1) -#+end_src -#+begin_details [Posterity / Disabled] Fancy Battery Setup -Let's have it also show remaining battery life, coloured green if charging and -coloured yellow otherwise. It is important to note that this package is no -longer maintained. It works on my machine. -#+BEGIN_SRC emacs-lisp :tangle no -;; Let's use a fancy indicator … -(use-package fancy-battery - :defer nil +Effort estimates are for an entire task. +Yet, sometimes it's hard to even get started on some tasks. - :custom (fancy-battery-show-percentage t) - (battery-update-interval 15) - :config (fancy-battery-mode)) -#+END_SRC -#+end_details ++ The code below ensures a 25 minute timer is started whenever clocking in happens. -*** Time & date -:PROPERTIES: -:CUSTOM_ID: Time-date -:END: -Let’s display the current time, with updates every second. + - The timer is in the lower right of the modeline. -#+begin_src emacs-lisp :tangle init.el -;; Show date and time as well. ++ When the timer runs out, we get a notification. -;; [Simple Approach] -;; (setq display-time-day-and-date t) -;; (display-time) ++ We may have the momentum to continue on the difficult task, or clock-out and + take a break after documenting what was accomplished. -;; [More Controlled Approach: Set date&time format] -;; a ≈ weekday; b ≈ month; d ≈ numeric day, R ≈ 24hr:minute. -(setq display-time-format "%a %b %d ╱ %r") ;; E.g.,: Fri Mar 04 ╱ 03:42:08 pm -(setq display-time-interval 1) ;; Please update the time every second. -(display-time-mode) -#+end_src +#+BEGIN_SRC emacs-lisp +;; Tasks get a 25 minute count down timer +(setq org-timer-default-timer 25) -But doc:display-time-mode shows me a bit more info that I actually don't care -for; so let's disable those. -#+begin_src emacs-lisp :tangle init.el -;; I don't need the system load average in the modeline. -(setq display-time-default-load-average nil) -(setq display-time-load-average nil) -#+end_src +;; Use the timer we set when clocking in happens. +(add-hook 'org-clock-in-hook + (lambda () (org-timer-set-timer '(16)))) + +;; unless we clocked-out with less than a minute left, +;; show disappointment message. +(add-hook 'org-clock-out-hook + (lambda () + (unless (s-prefix? "0:00" (org-timer-value-string)) + (message-box "The basic 25 minutes on this difficult task are not up; it's a shame to see you leave.")) + (org-timer-stop))) +#+END_SRC + +Note that this does not conflict with the total effort estimate for the task. + +(I'm told there's a package already made for this ---maybe I need to stop writing +code, and do more searches; then again, I've learned a lot by writing code.) -*** TODO COMMENT Column Numbers +** Journaling :PROPERTIES: -:CUSTOM_ID: Column-Numbers +:CUSTOM_ID: Journaling +:header-args: :tangle no :END: -Likewise, let's have the modeline display column numbers, but not line numbers. -Instead, let's have line numbers on the side of the buffer; moreover let's have -a uniform width for displaying line numbers, rather than having the width grow -as necessary. -#+BEGIN_SRC emacs-lisp :tangle init.el -;; (column-number-mode t) ;; Enabled in doom-modeline by default -;; (line-number-mode t) ;; Not sure I want line numbers in modeline, since I have them in the left margin. -#+END_SRC -Line numbers are a /conventionally expected/ part of a user interface, but I've -realised that I seldom /need to see/ them. I can still jump to a line number -provided by a compilation error with kbd:M-g_g; and toggle line numbers on when -I'm pair programming with doc:display-line-numbers-mode. -- In Emacs, there are /buffer/ which exist and contain textual data, but to - actually see them one requires a /window/. In the same vein, there are line - numbers but I don't need to always see them. -- If I need an indication of ‘progress’, the modeline contains a percentage of - how far I am in a buffer. -#+BEGIN_SRC emacs-lisp :tangle init.el -;; (setq display-line-numbers-width-start t) -;; (global-display-line-numbers-mode t) -(global-linum-mode -1) -#+END_SRC -** COMMENT Buffer names are necessarily injective :Maybe_no_longer_needed_due_to_doom: +*** Intro to why even journal :ignore: :PROPERTIES: -:CUSTOM_ID: Buffer-names-are-necessarily-injective +:CUSTOM_ID: Intro-to-why-even-journal :END: -By default when multiple files sharing the same name are opened, say for -comparison from different directories, their buffers are named uniquely by -having the format “⟨file-name⟩ <𝓃>”, for numbers 𝓃. It'd be more helpful -to have the buffer names reflect their location. -#+BEGIN_SRC emacs-lisp -;; Note that ‘uniquify’ is builtin. -(require 'uniquify) -(setq uniquify-separator "/" ;; The separator in buffer names. - uniquify-buffer-name-style 'forward) ;; names/in/this/style -#+END_SRC +Thus far I've made it easy to quickly capture ideas and tasks, not so much on +the analysis phase: - Note that this does not affect cloning buffers, ~C-x 4 c~. +- What was accomplished today? +- What are some notably bad habits? Good habits? +- What are some future steps? -( A function /f/ is /injective/ precisely when it's /distinction-preserving/; i.e., - /x ≠ y ≡ f x ≠ f y/. We can tell whether two things are the same or not, by - ‘zooming in’ on their particular property ‘f’, which may be easier to compare. - E.g., object IDs, hashcodes, unique keys in database tables. ) +Rather than overloading the capture mechanism for such thoughts, let's employ +~org-journal~ ---journal entries are stored in files such as ~journal/20190407~, +where the file name is simply the date, or only one file per year as I've set it +up below. Each entry is the week day, along with the date, then each child tree +is an actual entry with a personal title preceded by the time the entry was +made. Unlike capture and its agenda support, journal ensures entries are +maintained in chronological order with calendar support. -( Why am I bringing this up? I like math and seldom get to use it; so why not! ) +Since org files are plain text files, an entry can be written anywhere and later +ported to the journal. Or, written directly in the journal file if we add the +necessary Org-header: Asterisks and time. -** Exquisite Fonts and Themes +The separation of concerns is to emphasise the capture stage as being quick and +relatively mindless, whereas the journaling stage as being mindful. Even though +we may utilise capture to provide quick support for including journal entries, I +have set my journal to be on a yearly basis ---one file per year--- since I want +to be able to look at previous entries when making the current entry; after all, +it's hard to compare and contrast easily unless there's multiple entries opened +already. + +As such, ideally at the end of the day, I can review what has happened, and what +has not, and why this is the case, and what I intend to do about it, and what +problems were encountered and how they were solved ---in case the problem is +encountered again in the future. *Consequently, if I encounter previously +confronted situations, problems,* *all I have to do is reread my journal to get an +idea of how to progress.* Read more about [[https://www.google.com/search?q=on+the+importance+of+reviwing+your+day+daily&oq=on+the+importance+of+reviwing+your+day+daily&aqs=chrome..69i57.367j0j7&sourceid=chrome&ie=UTF-8][the importance of reviewing your day on +a daily basis]]. + +Moreover, by journaling with Org on a daily basis, it can be relatively easy to +produce a report on what has been happening recently, at work for example. I'd +like to have multiple journals, for work and for personal life, as such I will +utilise a prefix argument to obtain my work specific entries. + +*** The Setup :PROPERTIES: -:CUSTOM_ID: Exquisite-Fonts-and-Themes +:CUSTOM_ID: The-Setup :END: -Emacs' default theme leaves much to be desired: It does not look sleek and -shiny, which usually leaves first-timers with a poor, shallow, impression of the -system. Below we install a few themes that make Emacs look exquisite. We cycle -between the chosen themes with kbd:C-c_t, doc:my/toggle-theme. -#+begin_details “my/toggle-theme” Implementation -+ ~M-x load-theme RET TAB~ shows all themes, including built-in ones, - that may be loaded. -+ Loading multiple themes results in their pallets mixed. - - ~M-x disable-theme~ to remove a theme from the current pallet. +# As of Oct2022, Using org-journal breaks my permissions. E.g., org-time-stamp / C-c . results in " +# directory-files-recursively: Opening directory: Operation not permitted, /Users/musa/Desktop" --------------------------------------------------------------------------------- -#+begin_src emacs-lisp :tangle init.el -;; Treat all themes as safe; no query before use. -(setf custom-safe-themes t) +Anyhow, the setup: +#+begin_src emacs-lisp +(use-package org-journal -;; Nice looking themes ^_^ -(use-package solarized-theme :defer t) -(use-package doom-themes :defer t) -(use-package spacemacs-common - :defer t - :ensure spacemacs-theme) -(use-package stimmung-themes :defer t) -(use-package shanty-themes :defer t) + ;; C-u C-c j ⇒ Work journal ;; C-c j ⇒ Personal journal + :bind (("C-c j" . my/org-journal-new-entry)) + :config + (setq org-journal-dir "~/Desktop/" ;; "~/Dropbox/journal/" + org-journal-file-type 'yearly + org-journal-file-format "Personal-%Y-%m-%d.org") + + (defun my/org-journal-new-entry (prefix) + "Open today’s journal file and start a new entry. + With a prefix, we use the work journal; otherwise the personal journal." + (interactive "P") + (let ((org-journal-dir (if prefix "~/Desktop/" org-journal-dir)) + (org-journal-file-format (if prefix "Work-%Y-%m-%d.org" org-journal-file-format))) + (org-journal-new-entry nil) + (org-mode) + (org-show-all)))) #+end_src -+ The [[https://github.com/hlissner/emacs-doom-themes/tree/screenshots][Doom Themes]] also look rather appealing. -+ A showcase of many themes can be found [[https://emacsthemes.com/][here]]. +# Convenient org-journal Snippet Extensions +# Kill journal buffer after saving buffer (By @dhruvparamhans) +# (defun org-journal-save-entry-and-exit() +# "Simple convenience function. +# Saves the buffer of the current day's entry and kills the window +# Similar to org-capture like behavior" +# (interactive) +# (save-buffer) +# (kill-buffer-and-window)) +# (define-key org-journal-mode-map (kbd "C-x C-s") +# 'org-journal-save-entry-and-exit) +# --------------------------------------------------------------------------------- +*** Super Terse Tutorial +:PROPERTIES: +:CUSTOM_ID: Super-Terse-Tutorial +:END: -#+BEGIN_SRC emacs-lisp :tangle init.el -;; Infinite list of my commonly used themes. -(setq my/themes '(doom-laserwave shanty-themes-light stimmung-themes-light stimmung-themes-dark doom-solarized-light doom-vibrant spacemacs-light solarized-gruvbox-dark solarized-gruvbox-light)) -(setcdr (last my/themes) my/themes) -#+END_SRC +Bindings available in ~org-journal-mode~, when journaling: ++ ~C-c C-j~: Insert a new entry into the current journal file. + - Note that keys for ~org-journal-new-entry~ shadow those for ~org-goto~. ++ ~C-c C-s~: Search the journal for a string. + - Note that keys for ~org-journal-search~ shadow those for ~org-schedule~. -kbd:C-c_t to toggle between the personal themes. -#+BEGIN_SRC emacs-lisp :tangle init.el -(cl-defun my/load-theme (&optional (new-theme (completing-read "Theme: " (custom-available-themes)))) - "Disable all themes and load the given one ---read from user when called interactively." - (interactive) - (mapc #'disable-theme custom-enabled-themes) - (load-theme new-theme) - (message "Theme %s" new-theme)) +# C-c C-f - go to the next journal file. +# C-c C-b - go to the previous journal file. -(cl-defun my/toggle-theme (&optional (new-theme (pop my/themes))) - "Disable all themes and load NEW-THEME, which defaults from ‘my/themes’. +# Some key-bindings in org-journal overwrite org-mode key bindings -When a universal prefix is given, “C-u C-c t”, we load a random -theme from all possible themes. Nice way to learn about more -themes (•̀ᴗ•́)و" - (interactive) - (-let [theme (if current-prefix-arg - (nth (random (length (custom-available-themes))) - (custom-available-themes)) - new-theme)] - (my/load-theme theme))) +# Major modes are supposed to only use key bindings of the form C-c C-?, where ? +# can be any letter. With org-mode already using most interesting keys, +# collisions are inevitable. Currently, org-journal overwrites +# +# C-c C-f (org-forward-heading-same-level) with org-journal-open-next-entry +# C-c C-b (org-backward-heading-same-level) with org-journal-open-previous-entry +# -(global-set-key "\C-c\ t" 'my/toggle-theme) +All journal entries are registered in the Emacs Calendar. To see available +journal entries do ~M-x calendar~. Bindings available in the calendar-mode: -;; (my/toggle-theme) -(my/toggle-theme 'doom-laserwave) -#+END_SRC ++ ~j~: View an entry in a new buffer. + # C-j - view an entry but do not switch to it. ++ ~i j~: ‘I’nsert a new ‘j’ournal entry into the day’s file. ++ ~f w/m/y/f/F~: ‘F’ind, search, in all entries of the current week, month, year, all of time, + of in all entries in the future. +# +# [ - go to previous day with journal entries. +# ] - go to next day with journal entries. -Apparently, there's already a package that accomplishes these goals and more: -[[https://github.com/myTerminal/theme-looper][theme-looper]]. I may switch to it, but for now my simple function above is -slightly informative, to me at least, about how themes work and it does what I -want. -...Actually, the above learning adventure has made it easy to provide a similar setup -for fonts 😁 -#+end_details -Likewise, kbd:C-c_F, doc:my/toggle-font, to quickly change fonts (according to -mood 😸). [I already use kbd:C-c_f, doc:my/org-mode-format, for the more likely -operation of formatting text.] -#+begin_details “my/toggle-font” Implementation +All journal entries are registered in the Emacs Calendar. To see available journal entries do M-x calendar. Bindings available in the calendar-mode: +- [[kbd: j d]] display an entry; use [[kbd: j r]] to jump to the new reading buffer; + reading is in doc:view-mode: [[kbd:q]] to quit reading and [[kbd:SPC]] to scroll. ++ [[kbd: j s w/m/y/f]] search the journal entries of the current + week/month/year or for all time ++ ~[/]~ go the previous/next day with journal entries -#+begin_src emacs-lisp :tangle init.el -(unless nil ;; my/work-machine? +-------------------------------------------------------------------------------- - ;; Infinite list of my commonly used fonts - (setq my/fonts - '(;; NOPE: Breaks Gerrit! "Roboto Mono Light 14" ;; Sleek - "Input Mono 14" - "Source Code Pro Light 14" ;; thin, similar to Inconsolata Light - "Papyrus 14" - "Bradley Hand Light 12" - ;; "Chalkduster 14" ;; Laggy! - "Courier Light 12" - "Noteworthy 9" - "Savoye LET 14" - "Fantasque Sans Mono 16" - )) - (setcdr (last my/fonts) my/fonts) +*** Guided Journaling +:PROPERTIES: +:CUSTOM_ID: Guided-Journaling +:END: - ;; Let's ensure they're on our system - ;; brew search "/font-/" # List all fonts +Sometimes it can be tough to journal, but filling in a template can be a way to +get started. Later on, we will setup [[Snippets ---Template Expansion]] which will +allow us to write =journal_guided= then ~TAB~ to obtain the template below. Each =$𝓃= +indicates a position that we may input text, after which we ~TAB~ to move to next +location. - (shell-command "brew tap homebrew/cask-fonts") - (system-packages-ensure "svn") ;; Required for the following font installs - ;; No thanks! (system-packages-ensure "font-roboto-mono") ;; Makes Gerrit in Chrome look like Gibberish! - (system-packages-ensure "font-input") - (system-packages-ensure "font-source-code-pro") - (system-packages-ensure "font-fira-mono") - (system-packages-ensure "font-mononoki") - (system-packages-ensure "font-monoid") - (system-packages-ensure "font-menlo-for-powerline") - (system-packages-ensure "font-fantasque-sans-mono") +Just like the ~undo-tree~ setup at the start of this read, we use a =noweb-ref= +to present this template in a natural position; then later when template +expansion it setup, we request it to be tangled. -;; Use “M-x set-face-font RET default RET”, or... -;; (set-face-font 'default "Source Code Pro Light14") +#+begin_src org :noweb-ref templates-from-other-places-in-my-init :tangle no +,** journal_guided: Introspection & Growth +I'm writing from ${1:location}. -;; See ~2232 fonts -;; (append (fontset-list) (x-list-fonts "*" nil)) +Gut answer, today I feel ${2:scale}/10. +⇒ ${3:Few words or paragraphs to explain what's on your mind.} - (cl-defun my/toggle-font (&optional (new-font (pop my/fonts))) - "Load NEW-FONT, which defaults from ‘my/fonts’. +${4: All things which cause us to groan or recoil are part of the tax of +life. These things you should never hope or seek to escape. Life is a battle, +and to live is to fight. -When a universal prefix is given, “C-u C-c F”, we load a random -font from all possible themes. Nice way to learn about more -fonts (•̀ᴗ•́)و" - (interactive) - (let* ((all-fonts (append (fontset-list) (x-list-fonts "*" nil))) - (font (if current-prefix-arg - (nth (random (length all-fonts)) all-fonts) - new-font))) - (set-face-font 'default font) - (message "Font: %s" font))) +⟨ Press TAB once you've read this mantra. ⟩ +$(when yas-moving-away-p "") +} +`(progn + (eww "https://www.dailyinspirationalquotes.in/") + (sit-for 2) (when nil let eww load) + (read-only-mode -1) + (goto-line 52) + (kill-line) + (kill-buffer) + (yank))` +${7: +Self Beliefs: ++ I am working on a healthier lifestyle, including a low-carb diet. - (global-set-key "\C-c\ F" 'my/toggle-font) + - I’m also investing in a healthy, long-lasting relationship. - ;; Default font; the “ignore-⋯” is for users who may not have the font. - (ignore-errors (my/toggle-font "Fantasque Sans Mono 12")) - (ignore-errors (my/toggle-font "Source Code Pro Light 14"))) -#+end_src + ➩ These are what I want and are important to me. ⇦ -#+end_details ++ I will not use any substances to avoid real issues in my life. I must own them. -In any Org file, type ~elisp:menu-set-font~; then you can click on this link to -get a nice font selection menu ---this can be useful for your own ‘personal startup buffer’. ++ Everything I’m searching for is already inside of me. -# Finally, for fun, let's colour all source blocks, in Org mode, by the background colour pink. -# (set-face-attribute 'org-block nil :background "pink") -# MA: This does not work well with dark themes; should use a theme-based setting. ++ Progress is more important than perfection. -Let's use the following theme and font, upon startup. -#+begin_src emacs-lisp :tangle init.el -(unless noninteractive - ;; Breaks Gerrit: (my/toggle-font "Roboto Mono Light 14") - (my/toggle-theme 'solarized-gruvbox-light)) -#+end_src +⟨ Press TAB once you've read these beliefs. ⟩ +$(when yas-moving-away-p "") +} -** COMMENT Never lose the cursor -:PROPERTIES: -:CUSTOM_ID: Never-lose-the-cursor -:END: +*Three things I'm grateful for:* +1. ${8:??? … e.g., old relationship, something great yesterday, an opportunity I + have today, something simple near me within sight} +2. ${9:??? … e.g., old relationship, something great yesterday, an opportunity I + have today, something simple near me within sight} +3. ${10:??? … e.g., old relationship, something great yesterday, an opportunity I + have today, something simple near me within sight} -Let's have the entire line containing the cursour be slightly highlighted. -#+BEGIN_SRC emacs-lisp -;; Make it very easy to see the line with the cursor. -(global-hl-line-mode t) -#+END_SRC +*Three things that would make today great:* +1. ${11:???} +2. ${12:???} +3. ${13:???} -Moreover, we reduce the mental strain of locating the cursour when navigation happens: -When we switch windows or scroll, for example, we get a wave of light near the cursor. -#+begin_src emacs-lisp -(use-package beacon - :defer nil - :config (setq beacon-color "#666600") - :hook ((org-mode text-mode) . beacon-mode)) -#+end_src +*What one thing is top of mind today?* +${14:???} -** COMMENT Dimming Unused Windows -:PROPERTIES: -:CUSTOM_ID: Dimming-Unused-Windows -:END: +*What’s one opportunity I want to go after?* +${15:???} -Let's dim windows, and even the whole Emacs frame, when not in use. -#+BEGIN_SRC emacs-lisp -(use-package dimmer - :defer nil - :config (dimmer-mode)) -#+END_SRC +*What’s one thing I’m really proud of OR I’m amazed and in awe of?* +${16:???} -A more ‘fine-grained’ [[https://github.com/larstvei/Focus][tool]] dims all text except the ‘paragraph’ you're working -on. It's nifty, but not for me. +$0 +#+end_src -** COMMENT Flashing when something goes wrong +Besides a bit of webscraping to obtain a daily inspirational quote image, and +the necessary yasnippet code, this template was taken from a discussion on +[[https://news.ycombinator.com/item?id=20849148][Hacker news: “I find journaling indispensable”]]. In time, I will likely alter it +to meet my needs, but I like it as it is right now (•̀ᴗ•́)و + +** Workflow States :PROPERTIES: -:CUSTOM_ID: Flashing-when-something-goes-wrong +:CUSTOM_ID: Workflow-States :END: -Enable flashing mode-line on errors. E.g., ~C-g~, or calling an unbound key -sequence, or misspelling a word. -#+BEGIN_SRC emacs-lisp -;; (setq visible-bell 1) ;; On MacOS, this shows a caution symbol ^_^ - -;; The doom themes package comes with a function to make the mode line flash on error. -(use-package doom-themes :defer nil) -(require 'doom-themes-ext-visual-bell) -(doom-themes-visual-bell-config) -#+END_SRC - --------------------------------------------------------------------------------- +Here are some of my common workflow states, ---the ‘X/Y’ indicates to do action ‘X’ +when entering a state and ‘Y’ when leaving it, with ‘!’ denoting a timestamp +should be generated and ‘@’ denoting a user note should be made. +#+begin_src emacs-lisp +(setq org-todo-keywords + '((sequence "TODO(t)" "STARTED(s@/!)" "|" "DONE(d/!)") + (sequence "WAITING(w@/!)" "ON_HOLD(h@/!)" "|" "CANCELLED(c@/!)"))) -+A blinking cursor rushes me to type; let's slow down.+ Recently I'm thinking that -a blinking cursours prompts me to continue upwards and onwards. -#+BEGIN_SRC emacs-lisp -(blink-cursor-mode 1) -#+END_SRC +;; Since DONE is a terminal state, it has no exit-action. +;; Let's explicitly indicate time should be noted. +(setq org-log-done 'time) +#+end_src -** Hiding Scrollbar, tool bar, and menu -:PROPERTIES: -:CUSTOM_ID: Hiding-Scrollbar-tool-bar-and-menu -:END: +The ~@~ brings up a pop-up to make a local note about why the state changed. +*Super cool stuff!* -As a laptop user, screen space is important, so let's remove rarely used visual -items. -#+BEGIN_SRC emacs-lisp :tangle init.el -(unless noninteractive - (tool-bar-mode -1) ;; No large icons please - (scroll-bar-mode -1) ;; No visual indicator please - (menu-bar-mode -1)) ;; The Mac OS top pane has menu options -#+END_SRC +In particular, we transition from ~TODO~ to ~STARTED~ once 15 minutes, or a +reasonable amount, of work has transpired. Since all but one state are marked +for logging, we could use the ~lognotestate~ logging facility of org-mode, which +prompts for a note every time a task’s state is changed. -** TODO COMMENT Highlight & complete parenthesis pair when cursor is near ;-) -:PROPERTIES: -:CUSTOM_ID: Highlight-complete-parenthesis-pair-when-cursor-is-near -:END: +Entering a comment about what I've done, even if it's very little, feels like +I'm getting something done. It's an explicit marker of progress and motivates me +to want to change my task's states more often until I see it marked ~DONE~. -Highlight matching ‘parenthesis’ when near one of them. -#+begin_src emacs-lisp :tangle init.el -(setq show-paren-delay 0) -(setq show-paren-style 'mixed) -(show-paren-mode) +Here's how they are coloured, +#+begin_src emacs-lisp +(setq org-todo-keyword-faces + '(("TODO" :foreground "red" :weight bold) + ("STARTED" :foreground "blue" :weight bold) + ("DONE" :foreground "forest green" :weight bold) + ("WAITING" :foreground "orange" :weight bold) + ("ON_HOLD" :foreground "magenta" :weight bold) + ("CANCELLED" :foreground "forest green" :weight bold))) #+end_src -Colour parens, and other delimiters, depending on their depth. -Very useful for parens heavy languages like Lisp. -#+begin_src emacs-lisp :tangle init.el -(use-package rainbow-delimiters - :disabled - :hook ((org-mode prog-mode text-mode) . rainbow-delimiters-mode)) +Now we press ~C-c C-t~ then the letter shortcut to actually make the state of an org heading. +#+begin_src emacs-lisp +(setq org-use-fast-todo-selection t) #+end_src -For example: -#+begin_src emacs-lisp :tangle no -(blue (purple (forest (green (yellow (blue)))))) -#+end_src +We can also change through states using Shift- left, or right. +# (setq org-treat-S-cursor-todo-selection-as-state-change nil) -There is a powerful package called ‘smartparens’ for working with pair-able -characters, but I've found it to be too much for my uses. Instead I'll utilise -the lightweight package ~electric~, which Emacs provides out of the box. -#+BEGIN_SRC emacs-lisp :tangle init.el -(electric-pair-mode 1) -#+END_SRC -It supports, by default, ACSII pairs ~{}, [], ()~ and Unicode ~‘’, “”, ⟪⟫, ⟨⟩~. +Let's draw a state diagram to show what such a workflow looks like. -When writing Lisp, it is annoyong to have ‘<’ and ‘>’ be completed -/and/ considered as pairs. Let's disassociate them from both notions. -#+BEGIN_SRC emacs-lisp :tangle init.el -;; The ‘<’ and ‘>’ are not ‘parenthesis’, so give them no compleition. -(setq electric-pair-inhibit-predicate - (lambda (c) - (or (member c '(?< ?> ?~)) (electric-pair-default-inhibit c)))) +[[http://plantuml.com/index][PlantUML]] supports drawing diagrams in a tremendously simple format +---it even supports Graphviz/DOT directly and many other formats. +Super simple setup instructions can be found [[http://eschulte.github.io/babel-dev/DONE-integrate-plantuml-support.html][here]]; below are a bit more +involved instructions. Read the manual [[http://plantuml.com/guide][here]]. -;; Treat ‘<’ and ‘>’ as if they were words, instead of ‘parenthesis’. -(modify-syntax-entry ?< "w<") -(modify-syntax-entry ?> "w>") -#+END_SRC +#+begin_src emacs-lisp +;; Install the tool +; (async-shell-command "brew tap adoptopenjdk/openjdk; brew cask install adoptopenjdk13") ;; Dependency +; (async-shell-command "brew install plantuml") -:Rainbow_delims: -#+BEGIN_SRC emacs-lisp :tangle no -;; Act as usual unless a ‘<’ or ‘>’ is encountered. -;; ( char-at is really “character at poisition”; C-h o! ) -(setq rainbow-delimiters-pick-face-function - (lambda (depth match loc) - (unless (member (char-after loc) '(?< ?>)) - (rainbow-delimiters-default-pick-face depth match loc)))) -#+END_SRC -:End: +;; Tell emacs where it is. +;; E.g., (async-shell-command "find / -name plantuml.jar") +(setq org-plantuml-jar-path + "/usr/local/Cellar/plantuml/1.2022.14/libexec/plantuml.jar") -*Adding Org-emphasise markers for pair completion ---Disabled.* +;; Enable C-c C-c to generate diagrams from plantuml src blocks. +(add-to-list 'org-babel-load-languages '(plantuml . t) ) +(require 'ob-plantuml) -Let's add the org-emphasises markers: If we select a word then press =*=, it -becomes bold; likewise for ~/~ for emphasise. -#+BEGIN_SRC emacs-lisp :tangle no -(setq electric-pair-pairs - '((?~ . ?~) - (?* . ?*) - (?/ . ?/))) +; Use fundamental mode when editing plantuml blocks with C-c ' +(add-to-list 'org-src-lang-modes '("plantuml" . fundamental)) +#+end_src +# +# (async-shell-command "cp workflow.png ~/alhassy.github.io/assets/img/") -;; Let's also, for example, avoid obtaining double ‘~’ and ‘/’ when searching for a file. +Let's use this! +# The source block is replaced with the generated image in-place, by default. +# #+begin_src plantuml :file workflow.png :exports code :cache (progn (async-shell-command "cp workflow.png ~/alhassy.github.io/assets/img/") "yes") +#+begin_src plantuml :file images/workflow.png :tangle no :exports both :eval never-export :results replace +skinparam defaultTextAlignment center /' Text alignment '/ -;; Disable pairs when entering minibuffer -(add-hook 'minibuffer-setup-hook (lambda () (electric-pair-mode 0))) +skinparam titleBorderRoundCorner 15 +skinparam titleBorderThickness 2 +skinparam titleBorderColor red +skinparam titleBackgroundColor Aqua-CadetBlue +title My Personal Task States -;; Renable pairs when existing minibuffer -(add-hook 'minibuffer-exit-hook (lambda () (electric-pair-mode 1))) -#+END_SRC +[*] -> Todo /' This is my starting state '/ +Done -right-> [*] /' This is an end state '/ +Cancelled -up-> [*] /' This is an end state '/ -I use ‘~’ and ‘/’ too much during file navigation, and ‘*’ when marking numerous -Org headers, for which the ‘completed closing pair’ must tiresomely be deleted. -** COMMENT Proportional fonts for Headlines -:PROPERTIES: -:CUSTOM_ID: Proportional-fonts-for-Headlines -:END: +/'A task is “Todo”, then it's “started”, then finally it's “done”. '/ +Todo -right-> Started +Started -down-> Waiting +Waiting -up-> Started +Started -right-> Done -Let's have headings stick out a bit. -+ The larger headings are cute and reminicint of word processors, but having - headings coloured is enough ---the larger size is too much. -#+BEGIN_SRC emacs-lisp -(set-face-attribute 'org-document-title nil :height 2.0) -;; (set-face-attribute 'org-level-1 nil :height 1.0) -;; Remaining org-level-𝒾 have default height 1.0, for 𝒾 : 1..8. -;; -;; E.g., reset org-level-1 to default. -;; (custom-set-faces '(org-level-1 nil)) -#+END_SRC +/'Along the way, I may pause the task for some reason then + return to it. This may be since I'm “Blocked” since I need + something, or the task has been put on “hold” since it may not + be important right now, and it may be “cancelled” eventually. +'/ -Remember you can always use Emacs' Custom utility to get Lisp incantations ;-) ----See notes on Custom above. +Todo -down-> Waiting +Waiting -up-> Todo +Waiting -up-> Done -** Making Block Delimiters Less Intrusive -:PROPERTIES: -:CUSTOM_ID: Making-Block-Delimiters-Less-Intrusive -:END: +Todo -down-> On_Hold +On_Hold -> Todo -Let us render Org-mode's ~#+begin_src~ and ~#+end_src~ less obtrusively by, -e.g., having the former render as a pencil marker ~✎~ and the latter as a -tombstone ~□~ ---reminiscent of Halmos' QED end-of-proof marker. -# His setup also accounts for quotes. +On_Hold -down-> Cancelled +Waiting -down-> Cancelled +Todo -down-> Cancelled -#+begin_details Rasmus’ Incantation -This is from [[https://pank.eu/blog/pretty-babel-src-blocks.html#coderef-symbol][Rasmus Roulund]]. -#+begin_src emacs-lisp - (defvar-local rasmus/org-at-src-begin -1 - "Variable that holds whether last position was a ") +/' The Org-mode shortcuts for these states are as follows. '/ +Todo : t +On_Hold : h +Started : s +Waiting : w +Cancelled : c +Done : d - (defvar rasmus/ob-header-symbol ?☰ - "Symbol used for babel headers") +/' If a task is paused, we should document why this is the case. '/ +note right of Waiting: Note what is\nblocking us. +note right of Cancelled: Note reason\nfor cancellation. +note bottom of On_Hold: Note reason\nfor reduced priority. - (defun rasmus/org-prettify-src--update () - (let ((case-fold-search t) - (re "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*") - found) - (save-excursion - (goto-char (point-min)) - (while (re-search-forward re nil t) - (goto-char (match-end 0)) - (let ((args (org-trim - (buffer-substring-no-properties (point) - (line-end-position))))) - (when (org-string-nw-p args) - (let ((new-cell (cons args rasmus/ob-header-symbol))) - (cl-pushnew new-cell prettify-symbols-alist :test #'equal) - (cl-pushnew new-cell found :test #'equal))))) - (setq prettify-symbols-alist - (cl-set-difference prettify-symbols-alist - (cl-set-difference - (cl-remove-if-not - (lambda (elm) - (eq (cdr elm) rasmus/ob-header-symbol)) - prettify-symbols-alist) - found :test #'equal))) - ;; Clean up old font-lock-keywords. - (font-lock-remove-keywords nil prettify-symbols--keywords) - (setq prettify-symbols--keywords (prettify-symbols--make-keywords)) - (font-lock-add-keywords nil prettify-symbols--keywords) - (while (re-search-forward re nil t) - (font-lock-flush (line-beginning-position) (line-end-position)))))) +center footer ♥‿♥ Org-mode is so cool (•̀ᴗ•́)و +/' Note that we could omit the “center, left, right” if we wished, + or used a “header” instead.'/ +#+end_src - (defun rasmus/org-prettify-src () - "Hide src options via `prettify-symbols-mode'. +#+RESULTS: +[[file:images/workflow.png]] - `prettify-symbols-mode' is used because it has uncollpasing. It's - may not be efficient." - (let* ((case-fold-search t) - (at-src-block (save-excursion - (beginning-of-line) - (looking-at "^[ \t]*#\\+begin_src[ \t]+[^ \f\t\n\r\v]+[ \t]*")))) - ;; Test if we moved out of a block. - (when (or (and rasmus/org-at-src-begin - (not at-src-block)) - ;; File was just opened. - (eq rasmus/org-at-src-begin -1)) - (rasmus/org-prettify-src--update)) - ;; Remove composition if at line; doesn't work properly. - ;; (when at-src-block - ;; (with-silent-modifications - ;; (remove-text-properties (match-end 0) - ;; (1+ (line-end-position)) - ;; '(composition)))) - (setq rasmus/org-at-src-begin at-src-block))) +# (org-display-inline-images t t) +# (shell-command "rm workflow.png") +# +HTML: My Personal Task States - (defun rasmus/org-prettify-symbols () - (mapc (apply-partially 'add-to-list 'prettify-symbols-alist) - (cl-reduce 'append - (mapcar (lambda (x) (list x (cons (upcase (car x)) (cdr x)))) - `(("#+begin_src" . ?✎) ;; ➤ 🖝 ➟ ➤ ✎ - ("#+end_src" . ?□) ;; ⏹ - ("#+header:" . ,rasmus/ob-header-symbol) - ("#+begin_quote" . ?») - ("#+end_quote" . ?«))))) - (turn-on-prettify-symbols-mode) - (add-hook 'post-command-hook 'rasmus/org-prettify-src t t)) +Of note: ++ Multiline comments are with ~/' comment here '/~, single quote starts a one-line comment. -;; Last up­dated: 2019-06-09 -#+end_src -#+end_details ++ Nodes don't need to be declared, and their names may contain spaces if they are enclosed in double-quotes. -#+BEGIN_SRC emacs-lisp -(add-hook 'org-mode-hook #'rasmus/org-prettify-symbols) -(org-mode-restart) -#+END_SRC ++ One forms an arrow between two nodes by writing a line with ~x ->[label here] y~ + or ~y <- x~; or using ~-->~ and ~<--~ for dashed lines. The label is optional. -His development relies on built-in prettify-symbols-mode, which -disguises strings in a buffer for the sake of readability or -aesthetics. Following the example in the documentation, ~C-h f -prettify-symbols-mode~, we can quickly approximate his efforts for -~example~ blocks as follows, however a main issue is that source blocks -have busybodied headers which his setup disguises as ‘≡’. -#+begin_src emacs-lisp -(global-prettify-symbols-mode) + To enforce a particular layout, use ~-X->~ where ~X ∈ {up, down, right, left}~. -(defvar my/prettify-alist nil - "Musa's personal prettifications.") ++ To declare that a node ~x~ has fields ~d, f~ we make two new lines having + ~x : f~ and ~x : d~. -(cl-loop for pair in '(;; Example of how pairs like this to beautify org block delimiters - ("#+begin_example" . (?ℰ (Br . Bl) ?⇒)) ;; ℰ⇒ - ("#+end_example" . ?⇐) ;; ⇐ - ;; Actuall beautifications - ("==" . ?≈) ("===" . ?≈) ("=" . ?≔) ;; Programming specific prettifications - ("i32" . ?ℤ) ("u32" . ?ℕ) ("f64" . ?ℝ) ;; Rust specific - ("bool" . ?𝔹) - ("fn" . ?λ) - ("<=" . ?≤) (">=" . ?≥) - ("->" . ?→) ("-->". ?⟶) ;; threading operators - ("[ ]" . ?□) ("[X]" . ?☑) ("[-]" . ?◐)) ;; Org checkbox symbols - do (push pair my/prettify-alist)) ++ One adds a note near a node ~x~ as follows: ~note right of x: words then newline\nthen more words~. -;; Replace all Org [metadata]keywords with the “▷” symbol; e.g., “#+title: Hello” looks like “▷ Hello”. -(cl-loop for keyword in '(title author email date description options property startup export_file_name html_head fileimage filetags) - do (push (cons (format "#+%s:" keyword) ?▷) my/prettify-alist)) + Likewise for notes on the ~left, top, bottom~. -(cl-loop for hk in '(text-mode-hook prog-mode-hook org-mode-hook) - do (add-hook hk (lambda () - (setq prettify-symbols-alist - (append my/prettify-alist prettify-symbols-alist))))) -#+end_src + - A note can be on several lines. It's terminated by ~end note~. -#+RESULTS: ++ Interesting sprites and many other things can be done with PlantUML. Read the docs. -:Did_it_work: -#+begin_example lisp -(<= (+ 1 1) (--> 2)) +This particular workflow is inspired by [[http://doc.norang.ca/org-mode.html][Bernt Hansen]] ---while quickly searching +through the PlantUML [[http://plantuml.com/guide][manual]]: The above is known as an “activity diagram” and +it's covered in §4. -1 = -2 == -3 === -#+end_example -:End: +Org-mode may be used with PlantUML: ++ See §11,12 for using Org-mode notation to form ‘mindmaps’ and ‘work breakdown + structures’. -See [[http://www.modernemacs.com/post/prettify-mode/][“Mathematical Notation in Emacs”]] for how such prettifications can -make verbose (Python) scripts much more readable by employing more -economical disguises. ++ Org-mode text formatters are also acknowledged but the delimiters must be + doubled; see §16.1. -A nice sanity: -#+BEGIN_SRC emacs-lisp -;; Un-disguise a symbol when cursour is inside it or at the right-edge of it. -(setq prettify-symbols-unprettify-at-point 'right-edge) -#+END_SRC +You can quickly write and see the resulting UMLs using +https://liveuml.com/, for the most part. -** Hiding Emphasise Markers, Inlining Images, and LaTeX-as-PNG +** Clocking Work Time :PROPERTIES: -:CUSTOM_ID: Hiding-Emphasise-Markers-Inlining-Images-and-LaTeX-as-PNG +:CUSTOM_ID: Clocking-Work-Time :END: -:yay_it_worked: - -$e^x = \sum_{n = 0}^\infty \frac{x^n}{n!}$ - -~awkward~ or $not$ +Let's keep track of the time we spend working on tasks that we may have captured +for ourselves the previous day. Such statistics provides a good idea of how +long it actually takes me to accomplish a certain task in the future and it lets +me know where my time has gone. -:end: ++ Clock in :: on a heading with ~I~, or in the subtree with ~C-c C-x C-i~. ++ Clock out :: of a heading with ~O~, or in the subtree with ~C-c C-x C-o~. ++ Clock report :: See clocked times with ~C-c C-x C-r~. -Let's make some things prettier than they appear by default. -#+BEGIN_SRC emacs-lisp -;; org-mode math is now highlighted ;-) -(setq org-highlight-latex-and-related '(latex)) - -;; Extra space between text and underline line -(setq x-underline-at-descent-line t) - -;; Hide the *,=,/ markers -(setq org-hide-emphasis-markers t) - -;; Let’s limit the width of images inlined in org buffers to 400px. -(setq org-image-actual-width 400) - -;; Visually, I prefer to hide the markers of macros, so let’s do that: -;; {{{go(here)}}} is shown in Emacs as go(here) -(setq org-hide-macro-markers t) +After clocking out, the start and end times, as well as the elapsed time, are +added to a drawer to the heading. We can punch in and out of tasks as many times +as desired, say we took a break or switched to another task, and they will all +be recorded into the drawer. -;; On HTML exports, Org-mode tries to include a validation link for the exported HTML. Let’s disable that since I never use it. -;; (setq org-html-validation-link nil) +#+begin_src emacs-lisp +;; Record a note on what was accomplished when clocking out of an item. +(setq org-log-note-clock-out t) +#+end_src -;; Musa: This is super annoying, in practice. -(setq org-pretty-entities nil) ;; Also makes subscripts (x_{sub script}) and superscripts (x^{super script}) appear in org in a WYSIWYG fashion. -;; to have \alpha, \to and others display as utf8 -;; http://orgmode.org/manual/Special-symbols.html -;; -;; Be default, any consectuive string after “_” or “^” will be shown in WYSIWYG fashion; the following requires “^{⋯}” instead. -;; (setq org-use-sub-superscripts (quote {})) -#+END_SRC -Org pretty entities seems rather impressive ---=M-x org-entities-help= to see all -possibilities, or add your own. I'm already using the Agda input method, so I -wont use Org's ---Agda's gives me a tiny menu narrowing possibilities as I type. -However, it does make subscripts (x_{sub script}) and superscripts (x^{super script}) appear in Org in a WYSIWYG fashion. +To get started, we could estimate how long a task will take and clock-in; then +clock-out and see how long it actually took. --------------------------------------------------------------------------------- +# To review the day’s accomplishments, type ‘l’ +# (org-agenda-log-mode) from the agenda view. -Automatically display emphasis markers and links when the cursor is on them. -(c.f. ~fragtog~ below) +Sometimes, at the beginning at least, I would accidentally invoke the transposed +command ~C-x C-c~, which saves all buffers and quits Emacs. So here's a helpful +way to ensure I don't quit Emacs accidentally. #+begin_src emacs-lisp -(use-package org-appear - :disabled t - :hook (org-mode . org-appear-mode) - :init (setq org-appear-autoemphasis t - org-appear-autolinks nil - org-appear-autosubmarkers nil)) +(setq confirm-kill-emacs 'yes-or-no-p) #+end_src --------------------------------------------------------------------------------- - -The following is now disabled (yet again, as of Dec/31/2020) ---it makes my system slower than I'd like. -#+BEGIN_SRC emacs-lisp :tangle no -;; Show inline images when loading a new Org file. -(setq org-startup-with-inline-images t) - -;; Whenever a src block is run, redisplay images so they're up-to-date. -;; Very useful when using ‘ob-latex-as-png’, below. -(add-hook 'org-babel-after-execute-hook #'org-redisplay-inline-images) +A few more settings: +#+begin_src emacs-lisp +;; Resume clocking task when emacs is restarted +(org-clock-persistence-insinuate) -;; Automatically convert LaTeX fragments to inline images. -(setq org-startup-with-latex-preview t) -#+END_SRC +;; Show lot of clocking history +(setq org-clock-history-length 23) --------------------------------------------------------------------------------- +;; Resume clocking task on clock-in if the clock is open +(setq org-clock-in-resume t) -# latex-preview-in-org +;; Sometimes I change tasks I'm clocking quickly ---this removes clocked tasks with 0:00 duration +(setq org-clock-out-remove-zero-time-clocks t) -Org mode supports inline image previews of LaTeX fragments; e.g., $e^{i \cdot -\pi} - 1 = 0$ or $\substack{𝔹 \\ ↓ \\ 𝒜}$. These can be toggled with -kbd:C-c_C-x_C-l. [[https://github.com/io12/org-fragtog][Org-fragtog]] automates this, so fragment previews are disabled -for editing when your cursor steps onto them, and re-enabled when the cursor -leaves. +;; Clock out when moving task to a done state +(setq org-clock-out-when-done t) - #+BEGIN_SRC emacs-lisp -;; Automatically toggle LaTeX previews when cursour enters/leaves them -(use-package org-fragtog - :disabled t - :hook (org-mode . org-fragtog-mode)) - #+END_SRC +;; Save the running clock and all clock history when exiting Emacs, load it on startup +(setq org-clock-persist t) -doc:org-latex-preview, kbd:C-c_C-x_C-l, renders ~$e^{i \pi} + 1 = 0$~ into a -really nice inline image: $e^{i \pi} + 1 = 0$. It also works for LaTeX -environments ---for personal environments, just ~(add-to-list -'org-latex-packages-alist "LaTeX definitions here")~. +;; Do not prompt to resume an active clock +(setq org-clock-persist-query-resume nil) -#+begin_src emacs-lisp -;; Make previews a bit larger -(setq org-format-latex-options (plist-put org-format-latex-options :scale 1.5)) +;; Include current clocking task in clock reports +(setq org-clock-report-include-clocking-task t) +#+end_src -;; I use a lot of Unicode, so let's always include a unicode header. -(maybe-clone "https://armkeh.github.io/unicode-sty/") -(setq org-format-latex-header - (concat org-format-latex-header - "\n\\usepackage{\\string~\"/unicode-sty/unicode\"}")) -;; -;; Now this looks nice too! -;; $\substack{𝔹 \\ ↓ \\ 𝒜}$ and $\mathbb{B}$. +*** Finding tasks to clock in +:PROPERTIES: +:CUSTOM_ID: Finding-tasks-to-clock-in +:END: +Use one of the following options, with the top-most being the first to be tried. ++ From anywhere, ~C-u C-c C-x C-i~ yields a pop-up for recently clocked in tasks. ++ Pick something off today's agenda scheduled items. ++ Pick a ~Started~ task from the agenda view, work on this unfinished task. ++ Pick something from the ~TODO~ tasks list in the agenda view. -;; Always support unicode upon LaTeX export -;; No need to explicitly import armkeh's unicode-sty in each org file. -(add-to-list 'org-latex-packages-alist - "\n\\usepackage{\\string~\"/unicode-sty/unicode\"}") -#+end_src +# Reporting activities +# C-c C-x i -This approach does not work well for forming diagrams; I've tried to make tikzcd -work this way and failed. Using ~ob-latex-as-png~ as a substitute. -# work this way and failed. Using [doc : org-babel-execute:latex-as-png][ob-latex-as-png] as a substitute. -:calc: -#+begin_src emacs-lisp :tangle no -;; \step should be local to \begin{calc}⋯\end{calc}! -(add-to-list 'org-latex-packages-alist -"\\def\\BEGINstep{ \\{ } -\\def\\ENDstep{ \\} } -\\newcommand{\\step}[2][=]{ \\\\ #1 \\;\\; & \\qquad \\color{maroon}{\\BEGINstep \\text{ #2 } \\ENDstep} \\\\ & } -\\newenvironment{calc}{\\begin{align*} & }{\\end{align*}}") +~C-c C-x C-d~ also provides a quick summary of clocked time for the current org file. -; (pop org-latex-packages-alist) +*** Estimates versus actual time +:PROPERTIES: +:CUSTOM_ID: Estimates-versus-actual-time +:END: +Before clocking into a task, add to the properties drawer ~:Effort: 1:25~ or ~C-c +C-x C-e~, for a task that you estimate will take an hour and twenty-five minutes, +for example. Now the modeline will mention the time elapsed alongside the task +name. *Woah!* -;; See also org-format-latex-header +#+begin_src emacs-lisp + (push '("Effort_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00") + org-global-properties) #+end_src -:End: -# LaTeX Rendering: Support “latex-as-png” src blocks, which show LaTeX as PNGs -# LaTeX-Rendering-Support-latex-as-png-src-blocks-which-show-LaTeX-as-PNGs +#+begin_quote org +Use speed keys ~e/E~ to insert an effort estimate, with the above being provided + options, or to increment the current effort to the next one in the above list. +#+end_quote - #+BEGIN_SRC emacs-lisp -;; Support “latex-as-png” src blocks, which show LaTeX as PNGs -(use-package ob-latex-as-png :disabled t) - #+END_SRC +This is also useful when you simply want to put a time limit on a task that + wont be completed anytime soon, say writing a thesis or a long article, but + you still want to work on it for an hour a day and be warned when you exceed + such a time constraint. --------------------------------------------------------------------------------- + :Not_working_for_me: + Even if you switch tasks then clock into this task again, the alarm will ring + again, nagging you to actual listen to yourself and work on other matters. -Use ~ref:my-stuff~ to refer to an Org entity with ~#+name: my-stuff~; which must -have a ~#+caption: ⋯~ as well. Example entities include tables and source -blocks; as well as figure blocks. For equation blocks, you must use a -~\label{⋯}~ directly. + #+begin_src emacs-lisp +(setq org-clock-sound "~/.emacs.d/school-bell.wav") +#+end_src + :end: - #+begin_src emacs-lisp -;; Use the “#+name” the user provides, instead of generating label identifiers. -(setq org-latex-prefer-user-labels t) - #+end_src + When you've gone above your estimate time, the modeline colours it red. -** COMMENT Show off-screen heading at the top of the window +** Habit Formation :PROPERTIES: -:CUSTOM_ID: Show-off-screen-heading-at-the-top-of-the-window +:CUSTOM_ID: Habit-Formation :END: -In case we forgot which heading we're under, let's keep -the current heading stuck at the top of the window. -#+BEGIN_SRC emacs-lisp - (use-package org-sticky-header - :defer nil - :hook (org-mode . org-sticky-header-mode) - :config - (setq-default - org-sticky-header-full-path 'full - ;; Child and parent headings are seperated by a /. - org-sticky-header-outline-path-separator " / ")) -#+END_SRC -** COMMENT Powerful Directory Editing with ~dired~ -:PROPERTIES: -:CUSTOM_ID: Powerful-Directory-Editing-with-dired -:END: +/The/ reason to use habits is that they come with a graph indicating consistency +by colour, and the goal of the game is to have [[https://lifehacker.com/jerry-seinfelds-productivity-secret-281626][the longest possible chain]] ---no +red days! -⟨ ~C-x C-v~ to open a file or directory in dired, using the current buffer. ⟩ +A ‘habit’ is a usual (recurring) todo task marked as a habit: +Use =C-c C-x p= to set the =STYLE= property to =habit= on a task to set it as a habit. -As mentioned earlier, ~dired~ is Emacs' built-in directory editor; it's opened -with ~C-x d~. /Dired let's us treat directories as textual objects!/ In dired, -press ~h~ to see the many actions available. Here's a few... +#+BEGIN_SRC emacs-lisp +;; Show habits for every day in the agenda. +(setq org-habit-show-habits t) +(setq org-habit-show-habits-only-for-today nil) -#+begin_details Super Terse ‘dired’ Tutorial +;; This shows the ‘Seinfeld consistency’ graph closer to the habit heading. +(setq org-habit-graph-column 90) -+ ~(~ toggles hiding entry details, such as modification date and ownership -+ ~s~ sort entries; modeline will display “Dired by date” or “Dired by name”. -+ ~o~ to open entry in anOther window; or ~RET~ to open in place. -+ ~+~ to create a new directory; or ~M-x make-directory~. -+ ~/~ to filter entries; with ~which-key~, possible completions pop-up. - - E.g., ~/ f~ shows only files or ~/ . png~ to obtain all entries with extension - ~png~. - - ~/ i g~ to hide git-ignored items ^_^ - - ~/ /~ to remove all filters. -+ ~TAB~ to navigate between different groupings of entries. - - ~RET~ on a drawer heading toggles folding it ^_^ -#+end_details +;; In order to see the habit graphs, which I've placed rightwards, let's +;; always open org-agenda in ‘full screen’. +;; (setq org-agenda-window-setup 'only-window) +#+END_SRC -The [[https://github.com/Fuco1/dired-hacks#dired-hacks-utils][dired-hacks]] family of packages lets us, say, get a dired buffer out of a shell -incantation that lists files, or use dired to open files with external tools. -Below we use three of its packages. +| /inch by inch anything's a cinch!/ | -Pressing ~i~ inserts a directory's children under it, indented, in the current -buffer. Useful to see what's there. -#+BEGIN_SRC emacs-lisp -(use-package dired-subtree - :defer nil - :bind (:map dired-mode-map - ("i" . dired-subtree-toggle))) -#+END_SRC +~!~ means today and ~⋆~ means a task has been done on that day; +intuitively green means you're on track, yellow is warning sign of overdue, +red is overdue, and blue is an acceptable break day. -When directory ~𝒳~ has only one child ~𝒴~, then in dired, instead of ~𝒳~, show me ~𝒳/𝒴~ -with ~𝒳~ greyed out. -#+BEGIN_SRC emacs-lisp -(use-package dired-collapse - :defer nil - :hook (dired-mode . dired-collapse-mode)) -#+END_SRC +Here's an example habit from the [[https://orgmode.org/manual/Tracking-your-habits.html][Org-mode manual]], where ~.+𝒳d/𝒴d~ reads +/perform the habit once every 𝒳 days, but never let me go 𝒴 days without doing it./ +#+begin_example org +,** TODO Shave + SCHEDULED: <2020-01-08 Wed .+2d/4d> + :PROPERTIES: + :STYLE: habit + :END: +#+end_example +/Shave every 2 days, but we can take a 3-day break; however, on the 4th day, +gotta shave!/ (To “ignore” a habit, just reschedule it for another day.) -Begin dired with certain entries grouped together, according to some filtering -requirement; and with “garbage” files not shown ---i.e., those ending in -~.aux, .out~, etc. -#+BEGIN_SRC emacs-lisp -(use-package dired-filter - :defer nil - :hook (dired-mode . (lambda () (dired-filter-group-mode) - (dired-filter-by-garbage))) - :custom - (dired-garbage-files-regexp - "\\(?:\\.\\(?:aux\\|bak\\|dvi\\|log\\|orig\\|rej\\|toc\\|out\\)\\)\\'") - (dired-filter-group-saved-groups - '(("default" - ("Org" (extension "org")) - ("Executables" (exexutable)) - ("Directories" (directory)) - ("PDF" (extension "pdf")) - ("LaTeX" (extension "tex" "bib")) - ("Images" (extension "png")) - ("Code" (extension "hs" "agda" "lagda")) - ("Archives"(extension "zip" "rar" "gz" "bz2" "tar")))))) -#+END_SRC +Remember that in the agenda view if you alter a task, say with ~t~ to mark it +done, then you need to use ~s~ to save the underlying todo/notes files; otherwise, +any ~g~ will revert the change in the agenda buffer. -#+begin_details [Disabled] Neotree: Traditional Directory Tree Navigation -link-here:Neotree-Traditional-Directory-Tree-Navigation +** Actually Doing Things ---or /Sending notifications from Emacs/ +:PROPERTIES: +:CUSTOM_ID: Actually-Doing-Things +:END: -We open a nifty file manager upon startup. -#+BEGIN_SRC emacs-lisp :tangle no -;; Sidebar for project file navigation -(use-package neotree - :defer t - :disabled - :config (global-set-key "\C-x\ d" 'neotree-toggle) - (setq neo-theme 'icons)) ;; Uses all-the-icons from § Booting Up +Let's setup a little audio-visual reminder to regularly check my agenda +and ensure I'm not narrowing on a single task and ignoring others. -;; Open it up upon startup. -;; (neotree-toggle) -#+END_SRC -By default ~C-x d~ invokes ~dired~, but I prefer ~neotree~ for file -management. ++ More generally, we can use this snippet of code to let Emacs notify us of + other things. - ⟨ Edit: As a naive user, this is what I thought; yet a year later, - I've almost never used neotree. ⟩ ++ For instance, the doc:message function shows text in the minibuffer, which + might be missed when there are multiple incoming messages or focus is on a + non-Emacs application (gasp!). Then, doc:my/notify could be used to produce + MacOS system-wide notifications. -Useful navigational commands include -+ ~U~ to go up a directory. -+ ~C-c C-c~ to change directory focus; ~C-C c~ to type the directory out. -+ ~?~ or ~h~ to get help and ~q~ to quit. +The [[https://askubuntu.com/a/501917][text-to-speech tool]] we'll use is ~say~; which [[https://www.lifewire.com/mac-say-command-with-talking-terminal-2260772#:~:text=In%20the%20left%20pane%2C%20select,voices%20your%20Mac%20can%20use.][on a Mac can be activated]] +in a browser: Select some text, right-click, select ~Speech~, then ~Start/Stop Speaking~. +- TODO: Make the command below randomly use an English speaking voice; “tldr say” to learn more. +- TODO: Also finish reading the above mentioned links, with nice examples. -As always, to go to the neotree pane when it's the only other window, -execute ~C-x o~. +#+begin_src emacs-lisp +;; Obtain a notifications and text-to-speech utilities +(system-packages-ensure "say") ;; Built-into MacOS, but can be downloaded in Ubuntu +(system-packages-ensure "terminal-notifier") ;; MacOS specific +;; System Preferences → Notifications → Terminal Notifier → Allow “alerts”. +;; E.g.,: (shell-command "terminal-notifier -title \"Hiya\" -message \"hello\"") +#+end_src -I /rarely/ make use of this feature; company mode & Helm together quickly provide -an automatic replacement for nearly all of my uses. +By default, notifications are in banner style ---they go away automatically--- +we can use alert style ---in which they stay until dismissed--- in MacOS as +follows: =System Preferences → Notifications → terminal-notifier → Alerts=. -+ Reminiscent of GUI file managers is [[https://github.com/ralesi/ranger.el#features][ranger]]; e.g., it has multi-column - display of parent directories along with a file preview mechanism. -#+end_details +#+begin_src emacs-lisp +(cl-defun my/notify (message &key (titled "") at repeat-every-hour open) + "Notify user with both an visual banner, with a beep sound, and a text-to-speech recitation. -** COMMENT Persistent Scratch Buffer -:PROPERTIES: -:CUSTOM_ID: Persistent-Scratch-Buffer -:END: +When the user clicks on the resulting notification, unless a +given OPEN url is provided, the Emacs application is brough into +focus. -The ~*scratch*~ buffer is a nice playground for temporary data or experiments. +MESSAGE and TITLE are strings; AT is a time string as expected of +`run-at-time' such as \"11.23pm\" or \"5 sec\"; REPEAT-EVERY-HOUR +is a floating-point number of hours to continuously repeat the +alert. OPEN is a URL that is opened when the user clicks the +notification. This can be a web or file URL, or any custom URL +scheme. -However, by default its contents are not saved --which may be an issue if we -have not relocated our playthings to their appropriate files. Whence let's save -& restore the scratch buffer by default. -#+BEGIN_SRC emacs-lisp -(use-package persistent-scratch - :defer t - ;; In this mode, the usual save key saves to the underlying persistent file. - :bind (:map persistent-scratch-mode-map - ("C-x C-s" . persistent-scratch-save))) -#+END_SRC +I initially used optional arguments, but realised that in due time +it would be more informative to use named arguments instead. -We might accidentally close this buffer, so we could utilise the following. -#+BEGIN_SRC emacs-lisp -(defun scratch () - "Recreate the scratch buffer, loading any persistent state." - (interactive) - (switch-to-buffer-other-window (get-buffer-create "*scratch*")) - (condition-case nil (persistent-scratch-restore) (insert initial-scratch-message)) - (org-mode) - (persistent-scratch-mode) - (persistent-scratch-autosave-mode 1)) +Example uses: -;; This doubles as a quick way to avoid the common formula: C-x b RET *scratch* +;; In 5 minutes from now, remind me to watch this neato video! +(my/notify \"🔔 Get things done! 📎 💻 \" + :open \"https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity\" + :at \"5 minutes\") + ;; :at \"5 sec\" -;; Upon startup, close the default scratch buffer and open one as specfied above -(ignore-errors (kill-buffer "*scratch*") (scratch)) -#+END_SRC +;; Remind me to exercise every 1.5hours; starting at 8:00am. +(my/notify \"Take a 5min break and get your blood flowing!\" + :titled \"Exercise\" + :at \"8:00am\" + :repeat-every-hour 1.5) -I use Org-mode often, so that's how I want things to appear. -#+BEGIN_SRC emacs-lisp -(setq initial-scratch-message (concat - "#+Title: Persistent Scratch Buffer" - "\n#\n# Welcome! This’ a place for trying things out." - "\n#\n# ⟨ ‘C-x C-s’ here saves to ~/.emacs.d/.persistent-scratch ⟩ \n\n")) -#+END_SRC +;; Actually getting things done! +(my/notify \"Is what you're doing actually in alignment with your goals? + Maybe it's time to do another task?\" + :titled \"Check your agenda!\" + :at \"10:00am\" + :repeat-every-hour 2) -** COMMENT Tabs :Disabled: -:PROPERTIES: -:CUSTOM_ID: Tabs -:END: +" + (run-at-time at ;; the time to make the alert + (when repeat-every-hour (* 60 60 repeat-every-hour)) + #'async-shell-command + (format "%s" (s-replace "\n" "" + (s-join " " (--map (format "%s" it) + `(terminal-notifier + -title ,(pp-to-string titled) + -message ,(s-replace "\\n" "\n" (pp-to-string message)) + ;; Play a random sound when the notification appears. See sound names with: ls /System/Library/Sounds + ;; Use the special NAME “default” for the default notification sound. + -sound ,(progn (require 'seq) (seq-random-elt (s-split "\n" (shell-command-to-string "ls /System/Library/Sounds")))) + ;; Don't create duplicates of the notification, just one instance; + ;; i.e., each notification belongs to a group and only one alert of the group may be present at any one time. + -group ,(pp-to-string titled) + ;; Activate the application specified by ID when the user clicks the notification. + -activate org.gnu.Emacs + ,@(when open `(-open ,(pp-to-string open))) + ;; Run the shell command COMMAND when the user clicks the notification. + ;; -execute COMMAND + ;; & ;; … and then speak! … + ;; NOTE:This was getting annyoning in the middle of work meetings. + ;; say ,(s-replace "\\n" " " (pp-to-string message)) + ))))))) +#+end_src -I really like my Helm-supported ~C-x b~, but the visial appeal of a [[https://github.com/manateelazycat/awesome-tab][tab bar]] for Emacs -is interesting. Let's try it out and see how long this lasts ---it may be like Neotree: -Something cute to show to others, but not as fast as the keyboard. +#+RESULTS: +: my/notify -#+BEGIN_SRC emacs-lisp :tangle no -(use-package awesome-tab - :disabled t - :quelpa (awesome-tab :fetcher git :url "https://github.com/manateelazycat/awesome-tab.git") - :config (awesome-tab-mode t)) +The following two actual uses cases are also mentioned in doc:my/notify +docstring, since I want the documentation to be self-contained. +#+begin_src emacs-lisp +;; (Emojis look terrible in Lisp; but much better when the alert is actually made!) -;; Show me /all/ the tabs at once, in one group. -(defun awesome-tab-buffer-groups () - (list (awesome-tab-get-group-name (current-buffer)))) -#+END_SRC +;; Remind me to exercise every 1.5hours; starting at 8:00am. +(my/notify "Take a 5min break and get your blood flowing!\n\t\t🚣 🏃‍♂️ 🧗‍♂️ 🧘‍♂️ 🏊 🏋 🚴‍♂️" + :titled "🤾‍♀️ Exercise 🚵‍♂️" + :at "8:00am" + :repeat-every-hour 1.5 + :open "https://www.youtube.com/watch?v=23tusPiiNZk&ab_channel=Motiversity") -It's been less than three days and I've found this utility to be unhelpful, to me anyhow. +;; Actually getting things done! +(my/notify "Is what you're doing actually in alignment with your goals? ✔️📉 + Maybe it's time to do another task? 📋" + :titled "📆 Check your agenda! 🔔" + :at "10:00am" + :repeat-every-hour 2) +#+end_src -An alternative is [[https://github.com/ema2159/centaur-tabs][centaur-tabs.]] ++ [[https://emacs.stackexchange.com/questions/3844/good-methods-for-setting-up-alarms-audio-visual-triggered-by-org-mode-events][Here]] is an approach to triggering audio-visual alarms from Org-mode events + ---using ~org-agenda-to-appt~. + ++ Emacs's built in [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Appointments.html][appointment notification facility]] can also be used as a alarm + clock via ~M-x appt-add~. + +The [[https://github.com/jcs-elpa/marquee-header][marquee-header]] package let's show messages as “horizontal moving text along +the top of the Emacs frame”, which is neat. +- Slightly related is [[https://github.com/jcs-elpa/logms][logms]], which let's us make ~message~ calls but we can also + /see/ the context/source where those calls were made; as well as a clickable + link back to the source. -** COMMENT Window resizing using the golden ratio :Disabled: +** Using Gnus for Gmail :Disabled: :PROPERTIES: -:CUSTOM_ID: Window-resizing-using-the-golden-ratio +:CUSTOM_ID: Using-Gnus-for-Gmail +:header-args: :tangle no :END: -Let's load the following package, which automatically resizes windows so that -the window containing the cursor is the largest, according to the golden ratio. -Consequently, the window we're working with is nice and large yet the other windows -are still readable. +:Alread_done_elsewhere: +Let's set the following personal Emacs-wide variables ---to be used in other +locations besides email. -#+begin_src emacs-lisp :tangle no -(use-package golden-ratio - :disabled t - :init (golden-ratio-mode 1)) +#+begin_src emacs-lisp +(setq user-full-name "Musa Al-hassy" + user-mail-address "alhassy@gmail.com") #+end_src -After some time this got a bit annoying and I'm no longer using this. - -** COMMENT Org-Emphasise for Parts of Words :Disabled: -:PROPERTIES: -:CUSTOM_ID: Org-Emphasise-for-Parts-of-Words -:END: - -From [[https://stackoverflow.com/a/24540651/3550444][stackoverflow]], the following incantation allows us to have -parts of works emphasied with org-mode; e.g., -/half/ed, ~half~ed, and right in the m*idd*le! Super cool stuff! +For some fun, run this cute method. #+BEGIN_SRC emacs-lisp :tangle no -(setcar org-emphasis-regexp-components " \t('\"{[:alpha:]") -(setcar (nthcdr 1 org-emphasis-regexp-components) "[:alpha:]- \t.,:!?;'\")}\\") -(org-set-emph-re 'org-emphasis-regexp-components org-emphasis-regexp-components) +(animate-birthday-present user-full-name) #+END_SRC +:End: -I've disabled this feature since multiple occurrences -of an emphasise marker are sometimes treated as one -lengthy phrase being emphasised. +By default, in Emacs, we may send mail: Write it in Emacs with ~C-x m~ ---or +doc:compose-mail---, then press ~C-c C-c~ to have it sent via your OS's default +mailing system ---mine appears to be Gmail via the browser. Or cancel sending +mail with ~C-c C-k~ ---the same commands for org-capturing, discussed earlier +(•̀ᴗ•́)و -** COMMENT Preview link under cursor -:PROPERTIES: -:CUSTOM_ID: Preview-link-under-cursor -:END: -When cursor sits on a URL/Image/File link, try to preview it in a tooltip. -- Useful to quickly preview files and images. -- See also: https://github.com/jcs-elpa/preview-it +Folowing [[https://eschulte.github.io/emacs24-starter-kit/starter-kit-gnus.html][this tutorial]], to send and read email in Emacs we use [[https://en.wikipedia.org/wiki/Gnus][GNUS]], which, like +GNU itself, is a recursive acronym: GNUS Network User Service. -#+begin_src emacs-lisp -(quelpa '(preview-it :repo "jcs-elpa/preview-it" :fetcher github)) -;; (global-preview-it-mode) -#+end_src + 1. Execute, rather place in your init: + #+begin_src emacs-lisp + (setq message-send-mail-function 'smtpmail-send-it) + #+end_src + Revert to the default OS mailing method by setting this variable to + ~mailclient-send-it~. -This also works nicely when I'm looking to open a file; e.g., ~C-x C-f -~/.emacs.d/.as TAB~ to preview my ~.aspell.en.pws~ (Emacs personal dictionary) file -in a tooltip. -# (Sadly, the preview closes my ~M-x~ session when previewing an org file.) -# Not entirely true; but it's happened with some org files. + # (gnutls-available-p) -Likewise, I'd like to preview line when executing the doc:goto-line / kbd:M-g_M-g command. -#+begin_src emacs-lisp -(quelpa '(goto-line-preview :repo "jcs-elpa/goto-line-preview" :fetcher github)) -(global-set-key [remap goto-line] 'goto-line-preview) -#+end_src + 2. Follow only the [[https://www.emacswiki.org/emacs/GnusGmail#toc1][quickstart here]]; namely, make a file named ~~/.gnus~ containing: + #+begin_src emacs-lisp :tangle ~/.gnus +;; user-full-name and user-mail-address should be defined -** COMMENT Replace phrases with nice SVG labels -:PROPERTIES: -:CUSTOM_ID: Replace-phrases-with-nice-SVG-labels -:END: +;; Get mail using port 993/IMAP/“Internet Message Access Protocol” +(setq gnus-select-method + '(nnimap "gmail" + (nnimap-address "imap.gmail.com") + (nnimap-server-port 993) + (nnimap-stream ssl))) -[[ https://github.com/rougier/svg-tag-mode][SVG tags mode]] let's us replase arbitrary regular expressions with beautiful SVG -images that can be /clicked/ to produce an action, and may have a tooltip to -provide contextual information. Essentially an alternative to the built-in -doc:font-lock-mode, which performs arbitrary syntax highlighting. -- For more power, use the ~svg-lib~ package. -- The docs have nice examples. [[https://github.com/rougier/svg-tag-mode/blob/main/examples/example-2.el][Here]] are more useful examples. +;; Send mail using port 587/SMTP/“Simple Mail Transfer Protocol” +(setq message-send-mail-function 'smtpmail-send-it + smtpmail-starttls-credentials '(("smtp.gmail.com" 587 nil nil)) + smtpmail-auth-credentials '(("smtp.gmail.com" 587 "alhassy@gmail.com" nil)) + smtpmail-default-smtp-server "smtp.gmail.com" + smtpmail-smtp-server "smtp.gmail.com" + smtpmail-smtp-service 587) + #+end_src + # (system-packages-install "starttls") + # (setq gnus-ignored-newsgroups "^to\.\|^[0-9. ]+\( \|$\)\|^["]"[#'()]") -# Below I setup a function, doc : my/svg-tag-declare-badge to /declaratively/ produce SVG badges. + 3. Get an email password for GNUS: + 1. Go to https://myaccount.google.com/security. + 2. Enable ~2-Step Verification~ + 3. Click on ~App passwords~, login, then generate a new password + with, say, name ~Emacs Gnus~. + 4. You will then obtain a secret password, the ~x~ marks below, which you insert + in a file named ~~/.authinfo~ as follows ---using your email address. + #+begin_src shell :tangle no + ​machine smtp.gmail.com login alhassy@gmail.com password xxxxxxxxxxxxxxxx port 587 + ​machine imap.gmail.com login alhassy@gmail.com password xxxxxxxxxxxxxxxx port 993 + default login alhassy@gmail.com password xxxxxxxxxxxxxxxx + #+end_src -#+begin_src emacs-lisp -(use-package svg-tag-mode - :defer nil - :hook (org-mode prog-mode) - ;; :config (global-svg-tag-mode) ;; Nope: Breaks xwidget-webkit-browse-url, issue#28. - :config - (cl-defun my/svg-tag-declare-badge (template face &optional tooltip-message-upon-hover) - ;; Example faces: 'org-level-1 'org-todo 'font-lock-doc-face - "Given a TEMPLATE of the shape \"𝑿❙𝒀\", make SVG badge whose tag is 𝑿 and label is 𝒀. + 4. In Emacs, ~M-x gnus~ to see what's there. - When `svg-tags-mode' is enabled, every occurence of \"\\(𝑿\\)\\(𝒀\\)\" - is replaced by an SVG image essentially displaying “[𝑿∣𝒀]” using the given FACE. - This badge can be clicked to show all instances in the buffer. - You can see the badges documentation / intentions / help-message when you hover over it; - to see TOOLTIP-MESSAGE-UPON-HOVER. + - Or compose mail with ~C-x m~ then send it with ~C-c C-c~. + - Press ~C-h m~ to learn more about message mode for mail composition; or + read the [[https://www.gnus.org/manual/message.pdf][Message Manual]]. - Both 𝑿 and 𝒀 are regeular expressions; “❙” serves as the SVG tag-label delimiter - ---i.e., it saves as from writing \"\\(𝑿\\)\\(𝒀\\)\". Moreover, the SVG is only active - when regexp \"\\(𝑿\\)\\(𝒀\\)\" matches an instance." + Only news groups with /unread mail/ are shown; to see all your groups (Gmail + ‘tags’), press ~A A~ (doc:gnus-group-list-active), then press ~u~ to toggle + (un)subscription to such groups and they will show up in the main group + buffer ---if they have /unread mail/. See [[https://sachachua.com/blog/2008/05/emacs-gnus-organize-your-mail/][here]] for a tutorial on splitting + mail groups, automatically or fancily filing them away. - ;; Append tooltip message with a notice on what happens upon click. - (--> "Click on me to all see occurrences of this badge, in the current buffer!" - (if tooltip-message-upon-hover (concat tooltip-message-upon-hover "\n\n" it) it) - (setq tooltip-message-upon-hover it)) +-------------------------------------------------------------------------------- - (-let [(tag label) (s-split "❙" template)] - (-let [click-to-show-all-buffer-occurrences `(lambda () (interactive) (occur (concat ,tag ,label)))] - ;; Make an SVG for the tag. - (push - (cons (format "\\(%s\\)%s" tag label) `((lambda (tag) (svg-tag-make (s-chop-suffix ":" (s-chop-prefixes '("[" "<" "/*") tag)) :face (quote ,face) :inverse t :margin 0 :crop-right t :crop-left nil)) - ,click-to-show-all-buffer-occurrences - ,tooltip-message-upon-hover)) - svg-tag-tags) - ;; Make an SVG for the label. - (push - (cons (format "%s\\(%s\\)" tag label) `((lambda (label) (svg-tag-make (s-chop-suffixes '("]" ">" "*/") label) :face (quote ,face) :crop-left t)) - ,click-to-show-all-buffer-occurrences - ,tooltip-message-upon-hover)) - svg-tag-tags)))) +#+BEGIN_SRC emacs-lisp +;; After startup, if Emacs is idle for 10 seconds, then start Gnus. +;; Gnus is slow upon startup since it fetches all mails upon startup. +(when my/personal-machine? + (run-with-idle-timer 10 nil #'gnus)) +#+END_SRC - ;; Let's start off empty; then declare badges below. - (setq svg-tag-tags nil) +Learn more by reading [[https://www.gnu.org/software/emacs/manual/html_mono/gnus.html#Top][The Gnus Newsreader Manual]]; also available within Emacs by +~C-h i m gnus~ (•̀ᴗ•́)و - ;; Using caps so that these stick-out somewhat even when svg-tags-mode is not present. - (my/svg-tag-declare-badge "TODO:❙.*" 'org-todo "This is something I would like to do, in the future.") - (my/svg-tag-declare-badge "SILLY:❙.*" 'error "I’m experimenting; don't forget to clean-up when you’re done!") - (my/svg-tag-declare-badge "HACK:❙.*" 'error "This works, but it’s far from ideal. Plan to clean this in the future.") - (my/svg-tag-declare-badge "FIXME:❙.*" 'org-todo "This is busted! Plan to fix this in the future.") - (my/svg-tag-declare-badge "NOTE:❙.*" 'org-done "Something to be aware of; to keep in mind.") +- Or look at the [[https://www.gnu.org/software/emacs/refcards/pdf/gnus-refcard.pdf][Gnus Reference Card]]. +- Or, less comprehensively, this [[https://github.com/redguardtoo/mastering-emacs-in-one-year-guide/blob/master/gnus-guide-en.org#subscribe-groups][outline]]. +- [[https://www.emacswiki.org/emacs/GnusTutorial][EmacsWiki]] has a less technical and more user friendly tutorial. +- Other possibly useful links: + + [[http://www.cataclysmicmutation.com/2010/11/multiple-gmail-accounts-in-gnus/][Multiple Gmail accounts in Gnus]] - ;; [In]Active Time stamps --- M-x org-time-stamp - (my/svg-tag-declare-badge "\\[2022-.* ❙.*]" 'org-done "This is an inactive time stamp. It does not trigger the parent entry to appear in the agenda.") - (my/svg-tag-declare-badge "<2022-.* ❙.*>" 'org-todo "This is an active time stamp. It causes the parent Org entry to appear in the agenda.") +-------------------------------------------------------------------------------- - ;; JavaScript Lint Rules: \* eslint (.*) */ - (my/svg-tag-declare-badge "/\\* eslint ❙.* \\*/" 'org-done "It looks like you’ deviating from common conventions: Tread cautiously!") +#+begin_details Super Terse Tutorial - ;; TODO: Make SVG tags for other interesting “2-part” pieces of textual information - ) +link-here:Super-Terse-Tutorial -;; If everything is setup, the following examples should look like SVGs. -;; NOTE: Do something -;; TODO: fix me later -;; HACK: hiya -;; FIXME: this thing is busted 🎭 -;; SILLY: start -;; SILLY: end -;; [2022-04-20 Sun 16:30] -;; <2022-04-20 Sun 16:30> -;; /* eslint eqeqeq: 0, curly: 2 */ -;; NOTE: Toggle svg-tags-mode; useful when experimenting with new tags. -;; (progn (svg-tag-mode-off) (svg-tag-mode-on)) + ⟨ See the [[https://www.gnu.org/software/emacs/refcards/pdf/gnus-refcard.pdf][GNUS Reference Card]]! ⟩ -;; NOTE: (my/toggle-line-fontification) works fine with svg-tag-mode :-) -#+end_src +In gnus, by default items you've looked at disappear ---i.e., are archived. +They can still be viewed in, say, your online browser if you like. +In the ~Group~ view, ~R~ resets gnus, possibly retriving mail or alterations +from other mail clients. ~q~ exits gnus in ~Group~ mode, ~q~ exits the particular +view to go back to summary mode. Only after pressing ~q~ from within a group +do changes take effect on articles ---such as moves, reads, deletes, etc. -* Prose -:PROPERTIES: -:CUSTOM_ID: Prose -:END: ++ Expected keys: ~RET~ enter/open an item, ~q~ quit and return to previous view, ~g~ + refresh view ---i.e., ‘g’et new articles. -Emacs can be setup with a spellchecker and other expected features of a word processing tool ----however these features apply Emacs-wide since nearly everything is -essentially text (•̀ᴗ•́)و ++ =RET=: Enter a group by pressing, well, the enter key. + - Use ~SPC~ to open a group and automatically one first article there. + - Use ~C-u RET~ to see all mail in a folder instead of just unread mail. -- Org-mode is a writer's best friend; it's large enough to deserve its own sections. ++ Only groups/folders with unread mail will be shown, use ~L/l~ to toggle between + listing all groups. ++ ~SPC, DEL~ to scroll forward and backward; or ~C-v, M-v~ as always. -** Whitespace -:PROPERTIES: -:CUSTOM_ID: Whitespace -:END: -Let's start off by cleaning-up any accidental trailing whitespace and in other -places upon save. -#+begin_src emacs-lisp -(add-hook 'before-save-hook 'whitespace-cleanup) -#+end_src ++ =G G=: Search mail at server side in the group buffer. + - Limit search to particular folders/groups by marking them with ~#~, or + unmarking them with ~M-#~. -See [[http://ergoemacs.org/emacs/whitespace-mode.html][here]] for making whitespace visible; including spaces, tabs, and newlines ++ ~/ /,a:~ Filter mail according to subject or author; there are many + other options, see [[https://www.gnu.org/software/emacs/manual/html_mono/gnus.html#Limiting][§3.8 Limiting]]. -** Formatting Text -:PROPERTIES: -:CUSTOM_ID: Formatting-Text -:END: ++ =d=: Mark an article as done, i.e., read it and it can be archived. -The following incantation, doc:my/org-mode-format, makes it so that we can -select some text then press kbd:C-c_f (to get a list of possible character -completions) then press the symbol we want our text to be surrounded with. ++ =!=: Mark an article as read, but to be kept around ---e.g., you have not + replied to it, or it requires more reading at a later time. -#+begin_details -#+begin_src emacs-lisp -(local-set-key (kbd "C-c f") #'my/org-mode-format) -(defun my/org-mode-format (&optional text) -"Surround selected region with the given Org emphasises marker. + This lets us read mail offline; cached mail is found at =~/News/cache/=. -E.g., if this command is bound to “C-c f” then the sequence -“C-c f b” would make the currenly selected text be bold. -Likewise, “C-c f *” would achieve the same goal. + #+BEGIN_SRC emacs-lisp :tangle "~/.gnus" + (setq gnus-use-cache 'use-as-much-cache-as-possible) +#+END_SRC -When you press “C-c f”, a message is shown with a list of -useful single-character completions. ++ =B m=: Move an article, in its current state, to another group ---i.e., + ‘label’ using Gmail parlance. -Note: “C-c f 𝓍”, for an unrecognised marker 𝓍, just inserts -the character 𝓍 before and after the selected text." - (interactive "P") ;; Works on a region - ; (message "b,* ⟨Bold⟩; i,/ ⟨Italics⟩; u,_ ⟨Underline⟩; c,~ ⟨Monotype⟩") - (message "⟨Bold b,*⟩ ⟨Italics i,/⟩ ⟨Underline u,_⟩ ⟨Monotype c,~⟩") - (let ((kind (read-char))) - ;; Map letters to Org formatting symbols - (setq kind (or (plist-get '(b ?\* i ?\/ u ?\_ c ?\~) - (intern (string kind))) - kind)) - (insert-pair text kind kind))) -#+end_src -#+end_details + - Something to consider doing when finished with an article. -** Fill-mode ---Word Wrapping -:PROPERTIES: -:CUSTOM_ID: Fill-mode-Word-Wrapping -:END: + To delete an article, simply move it to ‘trash’ ---of course this will delete it + in other mail clients as well. There is no return from trash. -In fill mode, when you type past the end of a line, Emacs automatically starts a new line, cleverly formatting -paragraphs. This is a powerful form of “word wrap”. + Emails can always be archieved ---never delete, maybe? -#+BEGIN_SRC emacs-lisp -(setq-default fill-column 120 ;; Let's avoid going over 120 columns - truncate-lines nil ;; I never want to scroll horizontally - indent-tabs-mode nil) ;; Use spaces instead of tabs -#+END_SRC - -Certain variables are sensibly local to a buffer, and so ~setq~ only alters their value for one buffer. Using ~setq-default~ -we change a variable's default value, in every buffer. + Anyhow, ~B m Trash~ is too verbose, let's just use ~t~ for “trash”: + #+BEGIN_SRC emacs-lisp +(with-eval-after-load 'gnus + (bind-key "t" + (lambda (N) (interactive "P") (gnus-summary-move-article N "[Gmail]/Trash")) + gnus-summary-mode-map)) -#+BEGIN_SRC emacs-lisp -;; Wrap long lines when editing text -(add-hook 'text-mode-hook 'turn-on-auto-fill) -(add-hook 'org-mode-hook 'turn-on-auto-fill) +;; Orginally: t ⇒ gnus-summary-toggle-header #+END_SRC -We may press ~M-q~ to cleverly redistribute the line breaks within any paragraph, thereby making it look better. With a -prefix argument, it justifies it as well ---i.e., pads extra white space to make the paragraph appear rectangular. - - Note that ~M-o M-s~ centres a line of text ;-) Fun stuff! - -Fill-mode is also known as “hard word wrapping”, which has the counterpart “soft word wrapping” … - -Visual line mode is built-in and provides support for editing by visual lines: Lines off the screen are visually word -wrapped, but logically remain one line. Moreover =C-a,e,k= operate on visual lines rather than logical lines. -#+begin_src emacs-lisp -;; Bent arrows at the end and start of long lines. -(setq visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow)) -(global-visual-line-mode 1) -#+end_src -Visual line mode is useful when I have way too many windows open or when using smaller frames. - -** Pretty Lists Markers -:PROPERTIES: -:CUSTOM_ID: Pretty-Lists-Markers -:END: + - Select and deselect many articles before + moving them by pressing ~#~ and ~M-#~, respectively, anywhere on the entry. -When writing, it's common to use ~+,-,*~ to enumerate unordered lists ----especially so in Org-mode wherein they denote structured text. Let's render -them visually as Unicode bullets. + - As usual, you can mark a region, =C-SPC=, then move all entries therein. -#+begin_src emacs-lisp -;; (x y z) ≈ (existing-item replacement-item positivity-of-preceding-spaces) -(cl-loop for (x y z) in '(("+" "◦" *) - ("-" "•" *) - ("*" "⋆" +)) - do (font-lock-add-keywords 'org-mode - `((,(format "^ %s\\([%s]\\) " z x) - (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ,y))))))) -#+end_src ++ =R, r=: Reply with sender's quoted text in place, or without but + still visible in an adjacent buffer. + - Likewise ~S W~ or ~S w~ to reply all, ‘wide reply’, with or without quoted text. + - ~C-c C-z~ Delete everything from current position till the end. + - ~C-c C-e~ Replace selected region with ‘[...]’; when omitting parts of quoted text. -** Fix spelling as you type ---thesaurus & dictionary too! -:PROPERTIES: -:CUSTOM_ID: Fix-spelling-as-you-type-thesaurus-dictionary-too -:END: ++ Press ~m~ to compose mail; or ~C-x m~ from anywhere in Emacs to do so. + - ~C-c C-c~ to send the mail. + - ~S D e~ to resend an article as new mail: Alter body, subject, etc, before + - ~C-c C-f~ to forward mail. + sending. -I would like to check spelling on the fly. - + ~C-;~ :: Cycle through corrections for word at point. - + ~M-$~ :: Check and correct spelling of the word at point - + ~M-x ispell-change-dictionary RET TAB~ :: To see what dictionaries are available. ++ ~C-c C-a~ to attach a file; it'll be embedded in the mail body as plaintext. + - Press ~o~ on an attachment to save it locally. +#+end_details -Install spell-checking application as well as a reliable English -dictionary, [[https://wordnet.princeton.edu/][WordNet]]. - #+begin_src emacs-lisp -(system-packages-ensure "aspell") -(system-packages-ensure "wordnet") - #+end_src +#+begin_details GNUS Prettifications -~flyspell-prog-mode~ enables spell checking for programming by only considering -comments and strings. +link-here:gnus-prettifications +Let's add the icon  near my mail groups ^_^ #+BEGIN_SRC emacs-lisp -(use-package flyspell - - :hook ((prog-mode . flyspell-prog-mode) - ((org-mode text-mode) . flyspell-mode))) -#+END_SRC - -Enabling fly-spell for text-mode enables it for org and latex modes since they -derive from text-mode. - -Flyspell needs a spell checking tool, which is not included in Emacs. We -install ~aspell~ spell checker using, say, homebrew via ~brew install aspell~. Note -that Emacs' ~ispell~ is the interface to such a command line spelling utility. -# See available dictionary via ~aspell dicts~. +;; Fancy icons for Emacs +;; Only do this once: +(use-package all-the-icons + :config (all-the-icons-install-fonts 'install-without-asking)) -#+BEGIN_SRC emacs-lisp -(setq ispell-program-name "/opt/homebrew/bin/aspell") -(setq ispell-dictionary "en_GB") ;; set the default dictionary -#+END_SRC +;; Make mail look pretty +(use-package all-the-icons-gnus + :disabled t + :config (all-the-icons-gnus-setup)) -[Disabled] Allow spelling support for CamlCase words like “EmacsIsCool”. -#+BEGIN_SRC emacs-lisp :tangle no -(setq ispell-extra-args '("--sug-mode=ultra" - "--run-together" - "--run-together-limit=5" - "--run-together-min=2")) +;; While we're at it: Make dired, ‘dir’ectory ‘ed’itor, look pretty +(use-package all-the-icons-dired + :disabled t + :hook (dired-mode . all-the-icons-dired-mode)) #+END_SRC -Let us select a correct spelling merely by clicking on a word ----for the rare days I have a mouse. +Next, let's paste in some [[http://groups.google.com/group/gnu.emacs.gnus/browse_thread/thread/a673a74356e7141f][eye-candy for Gnus]]: #+begin_src emacs-lisp -(eval-after-load "flyspell" - ' (progn - (define-key flyspell-mouse-map [down-mouse-3] #'flyspell-correct-word) - (define-key flyspell-mouse-map [mouse-3] #'undefined))) +(setq gnus-sum-thread-tree-vertical "│" + gnus-sum-thread-tree-leaf-with-other "├─► " + gnus-sum-thread-tree-single-leaf "╰─► " + gnus-summary-line-format + (concat + "%0{%U%R%z%}" + "%3{│%}" "%1{%d%}" "%3{│%}" + " " + "%4{%-20,20f%}" + " " + "%3{│%}" + " " + "%1{%B%}" + "%s\n")) #+end_src +#+end_details -Colour incorrect works; default is an underline. -#+BEGIN_SRC emacs-lisp -(global-font-lock-mode t) -(custom-set-faces '(flyspell-incorrect ((t (:inverse-video t))))) -#+END_SRC - -Finally, save to user dictionary without asking: -#+BEGIN_SRC emacs-lisp -(setq ispell-silently-savep t) -#+END_SRC - -Let's keep track of my personal word set by having it be in my version controlled -.emacs directory. [[http://aspell.net/man-html/Format-of-the-Personal-and-Replacement-Dictionaries.html][Note]] that the default location is ~~/.[i|a]spell.DICT~ for -a specified dictionary ~DICT~. -#+BEGIN_SRC emacs-lisp -(setq ispell-personal-dictionary "~/.emacs.d/.aspell.en.pws") -#+END_SRC - -Nowadays, I very rarely write non-literate programs, but if I do -I'd like to check spelling only in comments/strings. E.g., -#+BEGIN_SRC emacs-lisp -(add-hook 'c-mode-hook 'flyspell-prog-mode) -(add-hook 'emacs-lisp-mode-hook 'flyspell-prog-mode) -#+END_SRC - -Use the thesaurus Emacs frontend [[https://github.com/hpdeifel/synosaurus][Synosaurus]] to avoid unwarranted repetition. -#+begin_src emacs-lisp -(use-package synosaurus - :init (synosaurus-mode) - :config (setq synosaurus-choose-method 'popup) ;; 'ido is default. - (global-set-key (kbd "M-#") 'synosaurus-choose-and-replace)) +#+begin_details "Sending Mail with Lisp ---e.g., as a Bulk Mailer" +link-here:bulk-mailer +#+begin_src emacs-lisp :results replace :wrap template +(defun my/email (to subject body) + (compose-mail to subject) + (insert body) + (message-send-mail) ;; Appends info to the message buffer + ; (let ((kill-buffer-query-functions nil)) (kill-this-buffer)) + (ignore-errors (undo)) ;; Undo that addition + (message-kill-buffer) + (message "Send email to %s" to)) ;; Close that message buffer #+end_src -The thesaurus is powered by the Wordnet ~wn~ tool, which can be invoked without an -internet connection! -#+begin_src emacs-lisp -;; (shell-command "brew cask install xquartz &") ;; Dependency -;; (shell-command "brew install wordnet &") + +#+begin_src emacs-lisp :results replace :wrap template :tangle no +;; Example +(my/email (format "%s <%s>" user-full-name user-mail-address) ;; To + "Test" ;; Subject + "Why hello there!") ;; Email body #+end_src +#+end_details -Let's use Wordnet as a dictionary via the [[https://github.com/gromnitsky/wordnut][wordnut]] package. -#+BEGIN_SRC emacs-lisp -(use-package wordnut - :bind ("M-!" . wordnut-lookup-current-word)) +#+begin_details [Disabled] Auto-completing mail addresses -;; Use M-& for async shell commands. -#+END_SRC -Use ~M-↑,↓~ to navigate dictionary results, and ~wordnut-search~ for a new search. +# :CUSTOM_ID: Auto-completing-mail-addresses -An alternative to =wordnut= is to use the lightweight ~define-word~ package; which I -think is not ideal since it provides way less information. +In order to get going quickly, using [[https://github.com/redguardtoo/gmail2bbdb][gmail2bbdb]], let's convert our Gmail +contacts into a BBDB file ---the [[http://bbdb.sourceforge.net/][Insidious Big Brother Database]] is an +address-book application that we'll use for E-mail; if you want to use it as a +address-book application to keep track of contacts, notes, their organisation, +etc, then consider additionally installing [[https://github.com/emacs-helm/helm-bbdb][helm-bbdb]] which gives a nice menu +interface. -:PowerthesaurusCurrentlyNotWorking: -#+BEGIN_SRC emacs-lisp :tangle no -(load "~/dotfiles/.emacs.d/powerthesaurus.el") -(global-set-key (kbd "M-#") 'powerthesaurus-lookup-word-at-point) + - From the [[https://www.google.com/contacts][Gmail Contacts page]], obtain a =contacts.vcf= file by clicking “More -> + Export -> vCard format -> Export”. +- Run command =M-x gmail2bbdb-import-file= and select =contacts.vcf=; a ~bbdb~ file + will be created in my Dropbox folder. +- Press ~C-x m~ then begin typing a contact's name and you'll be queried about + setting up BBDB, say yes. -;; Website currently down ... https://github.com/SavchenkoValeriy/emacs-powerthesaurus/issues/6 -#+END_SRC -:End: -** COMMENT Touch Typing -:PROPERTIES: -:CUSTOM_ID: Touch-Typing -:END: +#+begin_src emacs-lisp :tangle no +(use-package gmail2bbdb -Use this game to help you learn to spell words that you're having trouble with; -e.g., I have a file ~~/Dropbox/spelling.txt~ with words I have trouble spelling, -which I open then run ~M-x typing-of-emacs~ in order to improve spelling said -words. -#+BEGIN_SRC emacs-lisp :tangle no -;; The Typing Of Emacs, a game. -(use-package typing-of-emacs :defer t - :quelpa (typing :fetcher wiki :url "https://www.emacswiki.org/emacs/typing.el")) -#+END_SRC + :custom (gmail2bbdb-bbdb-file "~/Dropbox/bbdb")) -Practice touch typing using [[https://github.com/hagleitn/speed-type][speed-type]]. -#+begin_src emacs-lisp -(use-package speed-type :defer t) -#+end_src -Running ~M-x speed-type-region~ on a region of text, or ~M-x speed-type-buffer~ on a -whole buffer, or just ~M-x speed-type-text~ will produce the selected region, buffer, -or random text for practice. The timer begins when the first key is pressed -and stats are shown when the last letter is entered. +(use-package bbdb + :after company ;; The “com”plete “any”thig mode is set below in §Prose + :hook (message-mode . bbdb-insinuate-gnus) + (gnus-startup-hook . bbdb-insinuate-gnus) + :custom (bbdb-file gmail2bbdb-bbdb-file) + (bbdb-use-pop-up t) ;; allow popups for addresses + :config (add-to-list 'company-backends 'company-bbdb)) + #+end_src -Other typing resources include: -+ [[https://www.emacswiki.org/emacs/TypingOfEmacs][Typing of Emacs]] ---an Emacs alternative to speed type, possibly more engaging. -+ [[https://alternativeto.net/software/klavaro/][Klavaro]] ---a GUI based yet language-independent typing tutor. - - I'm enjoying this tool in getting started with Arabic typing. -+ [[https://typing.io/][Typing.io]] is a tutor for coders: Lessons are based on open source code, such - some XMonad written in Haskell or Linux written in C. -+ [[https://www.gnu.org/software/gtypist/index.html#downloading][GNU Typist]] ---which is interactive in the terminal, so not ideal in Emacs--, - -To assist in language learning, it may be nice to have an Emacs -[[https://github.com/atykhonov/google-translate][interface]] to Google translate ---e.g., invoke ~google-translate-at-point~. -#+BEGIN_SRC emacs-lisp -(use-package google-translate - :defer t - :config - (global-set-key "\C-ct" 'google-translate-at-point)) -#+END_SRC - -Select the following then ~C-c t~, -#+begin_quote -Hey buddy, what're you up to? -#+end_quote -Then /detect language/ then /Arabic/ to obtain: -#+begin_quote -مرحباً يا صديقي ، ماذا تفعل؟ -#+end_quote -Neato 😲 +Here is an [[http://emacs-fu.blogspot.com/2009/08/managing-e-mail-addresses-with-bbdb.html][emacs-fu]] article on managing e-mail addressed with bbdb. +#+end_details -** Using a Grammar & Style Checker -:PROPERTIES: -:CUSTOM_ID: Using-a-Grammar-Style-Checker -:END: +#+begin_details [Disabled] Feeds to Blogs +link-here:gnus-feeds-to-blogs +One can easily subscribe to an RSS feed in Gnus: Just press ~G R~ in the group +buffer view, then follow the prompts. However, doing so programmatically is much +harder. Below is my heartfelt attempt at doing so ---if you want a feed reader +in Emacs that “just works”, then [[https://github.com/skeeto/elfeed][elfeed]] is the way to go. When all is said and +done, the code below had me reading Gnus implementations and led me to conclude +that /Gnus has a great key-based interface but a /poor programming interface/ ---or +maybe I need to actually read the manual instead of frantically consulting +source code. -[ A possibly better alternative is [[https://emacstil.com/til/2022/03/05/setting-up-vale-prose-linter-on-emacs/][Vale]]. ] +My homemade hack to getting tagged feeds programmatically into Gnus. +#+begin_src emacs-lisp :tangle no +;; Always show Gnus items organised by topic. +(add-hook 'gnus-group-mode-hook 'gnus-topic-mode) -Let's install [[https://github.com/mhayashi1120/Emacs-langtool][a grammar and style checker]]. -We get the offline tool from the bottom of the [[https://languagetool.org/][LanguageTool]] website, then relocate it -as follows. -#+BEGIN_SRC emacs-lisp -(use-package langtool - :defer t - :custom - (langtool-language-tool-jar - "~/Applications/LanguageTool-4.5/languagetool-commandline.jar")) -#+END_SRC +;; From Group view, press ^, then SPC on Gwene, then look for the site you want to follow. +;; If it's not there, add it via the web interface http://gwene.org/ +(add-to-list 'gnus-secondary-select-methods '(nntp "news.gwene.org")) +;; +;; E.g., http://nullprogram.com/feed/ uses an Atom feed which Gnus does not +;; support natively. But it can be found on Gwene. -Now we can run ~langtool-check~ on the subsequent grammatically incorrect -text ---which is from the LanguageTool website--- which colours errors in red, -when we click on them we get the reason why; then we may invoke -~langtool-correct-buffer~ to quickly use the suggestions to fix each correction, -and finally invoke ~langtool-check-done~ to stop any remaining red colouring. +(setq my/gnus-feeds + ;; topic title url + '(Emacs "C‘est La 𝒵" https://cestlaz.github.io/rss.xml + Emacs "Marcin Borkowski's Blog" http://mbork.pl?action=rss + Emacs "Howardism" http://www.howardism.org/rss.xml + Islam "Shia Islam Blogspot" http://welcometoshiaislam.blogspot.com/feeds/posts/default?alt=rss + Cats "Hedonistic Learning" http://www.hedonisticlearning.com/rss.xml + Cats "Functorial Blog" https://blog.functorial.com/feed.rss + Programming "Joel on Software" http://www.joelonsoftware.com/rss.xml + Haskell "Lysxia's Blog" https://blog.poisson.chat/rss.xml)) -#+begin_example org -LanguageTool offers spell and grammar checking. Just paste your text here -and click the 'Check Text' button. Click the colored phrases for details -on potential errors. or use this text too see an few of of the problems -that LanguageTool can detecd. What do you thinks of grammar checkers? -Please not that they are not perfect. Style issues get a blue marker: -It's 5 P.M. in the afternoon. The weather was nice on Thursday, 27 June 2017 ---uh oh, that's the wrong date ;-) -#+end_example +;; If fubared, then: +;; (ignore-errors (f-delete "~/News/" 'force) (f-delete "~/.newsrc.eld")) -By looking around the source code, I can do all three stages smoothly (•̀ᴗ•́)و -#+BEGIN_SRC emacs-lisp -;; Quickly check, correct, then clean up /region/ with M-^ -(eval-after-load 'langtool +;; Execute this after a Gnus buffer has been opened. (progn -(add-hook 'langtool-error-exists-hook - (lambda () - (langtool-correct-buffer) - (langtool-check-done))) +(use-package with-simulated-input) +(cl-loop for (topic title url) + in (-partition 3 my/gnus-feeds) + ;; url & topic are symbols, make them strings. + for url′ = (symbol-name url) + for topic′ = (symbol-name topic) + ;; Avoid spacing issues by using a Unicode ghost space “ ”. + for title′ = (gnus-newsgroup-savable-name (s-replace " " " " title)) + for input = (format "C-SPC C-a %s RET RET" title′) + do + ; cl-letf* (((symbol-function 'insert) (lambda (x) nil))) ;; see the (undo) below. + ;; Add the group + (with-simulated-input input + (gnus-group-make-rss-group url′)) + ;; Ensure it lives in the right topic category. + (if (equal 'no-such-topic (alist-get topic gnus-topic-alist 'no-such-topic nil #'string=)) + (push (list topic′ title′) gnus-topic-alist) ;; make topic if it doesnt exist + (setf (alist-get topic′ gnus-topic-alist 'no-such-topic nil #'string=) + (cons title′ (alist-get topic gnus-topic-alist 'no-such-topic nil #'string=))))) + ;; Acknowledgement + (message "Now switch into the GNUS group buffer, and refresh the topics; i.e., t t.")) -(global-set-key "\M-^" - (lambda () - (interactive) - (message "Grammar checking begun ...") - (langtool-check))))) -#+END_SRC + ;; The previous command performs an insert, since it's intended to be interactively + ;; used; let's undo the insert. + ; (undo-only) -The checking command is silent, we added a bit of comforting acknowledgement to the user. +;; (setq gnus-permanently-visible-groups ".*") +;; +;; Show topic alphabetically? The topics list is rendered in reverse order. +;; (reverse (cl-sort gnus-topic-alist 'string-lessp :key 'car)) +#+end_src -** Lightweight Prose Proofchecking +Ironically, I've decide that “no, I do not want to see my blogs in Emacs” for +the same reasons I do not activelly use ~M-x eww~ to browse the web in Emacs: I +like seeing the colours, fonts, and math symbols that the authours have labored +over to producing quality content. Apparently, I'm shallow and I'm okay with it +---but not that shallow, since I'm constantly pushing Emacs which looks ugly by +default but it's unreasonably powerful. +#+end_details +*** Capturing Mail as Todo/Notes :PROPERTIES: -:CUSTOM_ID: Lightweight-Prose-Proofchecking +:CUSTOM_ID: Capturing-Mail-as-Todo-Notes :END: -Let's [[https://github.com/bnbeckwith/writegood-mode][write good]]! +Sometime mail contains useful reference material or may be a self-contained +task. Rather than using our inbox as a todo-list, we can copy the content of the +mail and store it away in our todos/notes files. [[#Capturing-ideas-notes-without-interrupting-the-current-workflow][Capturing]], above, is a way to, +well, capture ideas and notes /without/ interrupting the current workflow. Above, +in the section on capturing, we define doc:my/org-capture-buffer which quickly +captures the contents of the current buffer as notes to store away. We use that +method in the article view of mail so that [[kbd:c]] captures mail content with the +option to provide additional remarks, and [[kbd:C]] to silently do so without +additional remarks. #+BEGIN_SRC emacs-lisp -(use-package writegood-mode - ;; Load this whenver I'm composing prose. - :hook (text-mode org-mode) - ;; Don't show me the “Wg” marker in the mode line - - ;; Some additional weasel words. - :config - (--map (push it writegood-weasel-words) - '("some" "simple" "simply" "easy" "often" "easily" "probably" - "clearly" ;; Is the premise undeniably true? - "experience shows" ;; Whose? What kind? How does it do so? - "may have" ;; It may also have not! - "it turns out that"))) ;; How does it turn out so? - ;; ↯ What is the evidence of highighted phrase? ↯ +(with-eval-after-load 'gnus + ;; Orginally: c ⇒ gnus-summary-catchup-and-exit + (bind-key "c" #'my/org-capture-buffer gnus-article-mode-map) + ;; Orginally: C ⇒ gnus-summary-cancel-article + (bind-key "C" + (lambda (&optional keys) + (interactive "P") (my/org-capture-buffer keys 'no-additional-remarks)) + gnus-article-mode-map)) #+END_SRC -Inspired by Matt Might's [[http://matt.might.net/articles/shell-scripts-for-passive-voice-weasel-words-duplicates/][3 shell scripts to improve your writing, or -"My Ph.D. advisor rewrote himself in bash"]], this Emacs interface -emphasises, via underline, the following weaknesses in writing ---so -that I can fix them or decide that they are appropriate for the -scenario. - -Sentences that cut out the following problems may become stronger ----by being more terse or precise. - -+ Weasel Words :: - Phrases that sound good without conveying information; - such as vague precision or subjective phrases. +Gnus’ default =c= only enables a bad habit: Subscribing to stuff that you don't +read, since you can mark all entries as read with one key. We now replace it +with a ‘c’apturing mechanism that captures the current message as a todo or note +for further processing. Likewise, the default =C= is to cancel posting an article; +we replace it to be a /silent capture: Squirrel away informative mail content +without adding additional remarks./ +*** Email contacts +:PROPERTIES: +:CUSTOM_ID: Email-contacts +:END: - E.g., /a number of, surprisingly, very close/. +I have a personal file, ~contacts.org~, with Emacs Lisp src blocks contributing to +a list variable ~my/contacts~. This list consists of entries of the shape: +#+begin_src emacs-lisp :tangle no +(:name "Jasim Jasonsama" :phone 123-455-4321 :email bobert_billiam@emacsmail.com) +#+end_src - It's okay not to have exact details, but rather than “I don't know” - explain why not and what the next steps will be. +With the following snippet, I can write ~contacts~ then kbd:TAB to select a +personal contact. +#+begin_src org :noweb-ref templates-from-other-places-in-my-init :tangle no :comments none +,** contacts: Get the email of one of my personal contacts -+ Passive Voice :: - Phrases wherein interest is in the object experiencing an action, - rather than the subject that performs the action. +${1:`(and (or (featurep 'my/contacts) (org-babel-load-file "~/Dropbox/contacts.org")) +(yas-choose-value (--map (format "%s <%s>" (getf it :name) (getf it :email)) +my/contacts)))`} $0 +#+end_src - - Bad: The house /was built by/ my father. - - Good: My father /built/ this house. +** [[https://github.com/sabof/stripe-buffer][Add stripes to "list" buffers]] +:PROPERTIES: +:CUSTOM_ID: https-github-com-sabof-stripe-buffer-Add-stripes-to-list-buffers +:END: - Likewise, including relevant or explanatory information as in “X - guarantees Y” is an improvement over “Y is guaranteed”. +#+begin_src emacs-lisp +;; Make every other line of a buffer grey (or whatever you like). +;; Useful for buffers that list things. +;; I want it to make my Org tables look nice. Even better when org-modern is activated. +(use-package stripe-buffer + :defer 100 + :config (add-hook 'org-mode-hook 'turn-on-stripe-table-mode)) +#+end_src - Sometimes the subject really is irrelevant, such as - “We did X” whereas “X happened” suffices. +* Lisp Programming +:PROPERTIES: +:CUSTOM_ID: Lisp-Programming +:END: - 👍 If the relevant subject is unclear and, also, the text reads - better in the active, then change a phrase. +** highlight quoted symbols +:PROPERTIES: +:CUSTOM_ID: highlight-quoted-symbols +:END: -+ Duplicated Words :: Occurrences of, say, “the the”. +#+begin_src emacs-lisp +(use-package highlight-quoted + :defer nil + :config (add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode)) - Harder to catch manually, but easier mechanically ;-) +;; If everything worked fine, then “ 'b ” below should be coloured nicely in Emacs Lisp mode. +(when nil + (-let [x 'somevar] + (list x 'b "c" :e))) +#+end_src -** Placeholder Text ---For Learning & Experimenting +** Get CheatSheets and view them easily :Disabled: :PROPERTIES: -:CUSTOM_ID: Placeholder-Text-For-Learning-Experimenting +:CUSTOM_ID: Get-CheatSheets-and-view-them-easily +:header-args: :tangle no :END: -When learning about Emacs formatting commands, such as zap-to-char ~M-z~ -or transpose ~M-t~, it's best to have filler text ---even better when -it's automatically generated instead of typing it out ourselves. The -following will give us a series of commands ~lorem-ipsum-insert-⋯~ for -inserting lists, sentences, paragraphs and using a prefix argument, -with ~C-u~, we can request to generate any number of them. - -#+BEGIN_SRC emacs-lisp -(use-package lorem-ipsum :defer t) -#+END_SRC +#+begin_src emacs-lisp +(defvar my/cheatsheet/cached-topics nil) +(cl-defun my/cheatsheet (&optional topic) + "Clone Al-hassy's ⟨TOPIC⟩CheatSheet repository when called from Lisp; visit the pretty HTML page when called interactively. -‘Lorem’ is not a word itself, but it comes from the Latin ‘Dolorem Ipsum’ -which means “pain in and of itself”. +- Example usage: (my/cheatsheet \"Vue\") +- Example usage: M-x my/cheatsheet RET Vue RET." + (interactive) + (if (not topic) + (browse-url (format "https://alhassy.github.io/%sCheatSheet" (completing-read "Topic: " my/cheatsheet/cached-topics))) + (push topic my/cheatsheet/cached-topics) + (maybe-clone (format "https://github.com/alhassy/%sCheatSheet" topic)))) +#+end_src -See this [[https://github.com/alhassy/emacs.d/blob/master/CheatSheet.pdf][Emacs Cheat Sheet]] to try out the textual navigation and formatting -bindings on lorem ipsum, gibberish text. +Let's actually get some repos locally, and use: ~M-x my/cheatsheet~ to view the pretty HTML (or PDF) sheets. +#+begin_src emacs-lisp +(mapcar #'my/cheatsheet '("ELisp" "GojuRyu" "Rust")) ; Python Prolog Vue Agda JavaScript + ; Clojure Ruby Oz Coq Cats Haskell FSharp OCaml +#+end_src -** Some text to make us smile +* Prose :PROPERTIES: -:CUSTOM_ID: Some-text-to-make-us-smile +:CUSTOM_ID: Prose :END: -The [[https://github.com/davep/dad-joke.el][dad-joke]] queries [[https://icanhazdadjoke.com][https://icanhazdadjoke.com]] to bring us some funny. +Emacs can be setup with a spellchecker and other expected features of a word processing tool +---however these features apply Emacs-wide since nearly everything is +essentially text (•̀ᴗ•́)و + +- Org-mode is a writer's best friend; it's large enough to deserve its own sections. + +- See [[https://lucidmanager.org/tags/emacs/][Emacs Writing Studio]] for a series of articles on making/using Emacs for writing. + +** Bidirectional Text #+begin_src emacs-lisp -(use-package dad-joke - :defer t - :config (defun dad-joke () (interactive) (insert (dad-joke-get)))) +;; Sometimes I have Arabic in my buffers, however I am an English speaker +;; and so Left-to-Right is most natural to me. As such, even when Arabic +;; is present, or any bidirectional text, just use Left-to-Right. +(setq-default bidi-paragraph-direction 'left-to-right) +#+end_src +** Whitespace +:PROPERTIES: +:CUSTOM_ID: Whitespace +:END: +Let's start off by cleaning-up any accidental trailing whitespace and in other +places upon save. +#+begin_src emacs-lisp +(add-hook 'before-save-hook 'whitespace-cleanup) #+end_src -For example, ~M-x dad-joke~ now inserts: -#+begin_quote org -What are the strongest days of the week? Saturday and Sunday...the rest are -weekdays. -#+end_quote +See [[http://ergoemacs.org/emacs/whitespace-mode.html][here]] for making whitespace visible; including spaces, tabs, and newlines -** Unicode Input via Agda Input +** Formatting Text :PROPERTIES: -:CUSTOM_ID: Unicode-Input-via-Agda-Input +:CUSTOM_ID: Formatting-Text :END: -:agda2-include-dirs-Issue: -#+BEGIN_SRC emacs-lisp -;; (load (shell-command-to-string "agda-mode locate")) -;; -;; Seeing: One way to avoid seeing this warning is to make sure that agda2-include-dirs is not bound. -; (makunbound 'agda2-include-dirs) -#+END_SRC -:End: - -[[https://mazzo.li/posts/AgdaSort.html][Agda]] is one of my favourite languages, it's like Haskell on steroids. Let's set -it up for the main sake of its Unicode input ---you may do likewise using TeX -input. ( [[https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/][The Absolute Minimum Every Software Developer Absolutely, Positively -Must Know About Unicode and Character Sets (No Excuses!)]] ) -/Agda input mode makes it extremely easy to use unicode in documents, something I/ -/strongly prefer to do. When I can use symbols directly, instead of (for/ -/instance) LaTeX commands, it makes my plaintext far more readable./ --- [[https://github.com/armkeh/dotfiles/tree/master/emacs][Armkeh -.emacs config]] +The following incantation, doc:my/org-mode-format, makes it so that we can +select some text then press kbd:C-c_f (to get a list of possible character +completions) then press the symbol we want our text to be surrounded with. +#+begin_details #+begin_src emacs-lisp -(system-packages-ensure "agda") -#+end_src +(local-set-key (kbd "C-c f") #'my/org-mode-format) +(defun my/org-mode-format (&optional text) +"Surround selected region with the given Org emphasises marker. -#+begin_details To use the Agda standard library by default - #+BEGIN_SRC shell :tangle no - mkdir -p ~/.agda - echo /usr/local/lib/agda/standard-library.agda-lib >>~/.agda/libraries - echo standard-library >>~/.agda/defaults - #+END_SRC - Invoke ~brew info agda~ to get these instructions and the version of Agda just - installed. -#+end_details +E.g., if this command is bound to “C-c f” then the sequence +“C-c f b” would make the currenly selected text be bold. +Likewise, “C-c f *” would achieve the same goal. -#+begin_details Get font support for subscripts, if need be - - Download and unzip the [[https://fontlibrary.org/en/font/symbola][symbola]] font - - kbd:CMD_+_SPC ⇒ =font book= ⇒ =+= ⇒ select the symbola directory you just - unzipped +When you press “C-c f”, a message is shown with a list of +useful single-character completions. - (Note: In the before time, you could brew install this font.) +Note: “C-c f 𝓍”, for an unrecognised marker 𝓍, just inserts +the character 𝓍 before and after the selected text." + (interactive "P") ;; Works on a region + ; (message "b,* ⟨Bold⟩; i,/ ⟨Italics⟩; u,_ ⟨Underline⟩; c,~ ⟨Monotype⟩") + (message "⟨Bold b,*⟩ ⟨Italics i,/⟩ ⟨Underline u,_⟩ ⟨Monotype c,~⟩") + (let ((kind (read-char))) + ;; Map letters to Org formatting symbols + (setq kind (or (plist-get '(b ?\* i ?\/ u ?\_ c ?\~) + (intern (string kind))) + kind)) + (insert-pair text kind kind))) +#+end_src #+end_details -Executing ~agda-mode setup~ appends the following text to the ~.emacs~ file. -Let's put it here ourselves. -#+begin_src emacs-lisp -(unless noninteractive - (load-file (let ((coding-system-for-read 'utf-8)) - (shell-command-to-string "agda-mode locate")))) -#+end_src +** Fill-mode ---Word Wrapping +:PROPERTIES: +:CUSTOM_ID: Fill-mode-Word-Wrapping +:END: + +In fill mode, when you type past the end of a line, Emacs automatically starts a new line, cleverly formatting +paragraphs. This is a powerful form of “word wrap”. -I almost always want the ~agda-mode~ input method ---it's like the TeX method, but -better. #+BEGIN_SRC emacs-lisp -;; MA: This results in "Package cl is deprecated" !? -(unless noninteractive - (use-package agda-input - :ensure nil ;; I have it locally. - :demand t - :hook ((text-mode prog-mode) . (lambda () (set-input-method "Agda"))) - :custom (default-input-method "Agda"))) - ;; Now C-\ or M-x toggle-input-method turn it on and offers +(setq-default fill-column 120 ;; Let's avoid going over 120 columns + truncate-lines nil ;; I never want to scroll horizontally + indent-tabs-mode nil) ;; Use spaces instead of tabs +#+END_SRC +Certain variables are sensibly local to a buffer, and so ~setq~ only alters their value for one buffer. Using ~setq-default~ +we change a variable's default value, in every buffer. -;; TODO add a hook that when the input method becomes Agda, just don't bother showing me in the modeline. -;; E.g., "Π" when using unicode input with Agda -;; Useful to have in the modeline, say when typing in Arabic. -;; (add-variable-watcher -;; 'current-input-method -;; (lambda (_ newvalue 'set _) -;; (setq current-input-method-title -;; (if (equal newvalue "Agda") nil newvalue)))) +#+BEGIN_SRC emacs-lisp +;; Wrap long lines when editing text +(add-hook 'text-mode-hook 'turn-on-auto-fill) +(add-hook 'org-mode-hook 'turn-on-auto-fill) #+END_SRC -:agda_Command_line_arguments: - "+RTS -H4.5G -M4.5G -K256M -S/tmp/AgdaRTS.log -A1G -RTS -i ." +We may press ~M-q~ to cleverly redistribute the line breaks within any paragraph, thereby making it look better. With a +prefix argument, it justifies it as well ---i.e., pads extra white space to make the paragraph appear rectangular. -Wolfram Kahl has recommended the following settings. + Note that ~M-o M-s~ centres a line of text ;-) Fun stuff! + +Fill-mode is also known as “hard word wrapping”, which has the counterpart “soft word wrapping” … +Visual line mode is built-in and provides support for editing by visual lines: Lines off the screen are visually word +wrapped, but logically remain one line. Moreover =C-a,e,k= operate on visual lines rather than logical lines. #+begin_src emacs-lisp -;;(setq agda2-program-args (quote ("RTS" "-M4G" "-H4G" "-A128M" "-RTS"))) +;; Bent arrows at the end and start of long lines. +(setq visual-line-fringe-indicators '(left-curly-arrow right-curly-arrow)) +(global-visual-line-mode 1) #+end_src +Visual line mode is useful when I have way too many windows open or when using smaller frames. -These arguments specify -| ~+RTS~, ~-RTS~ | Flags between these are arguments to the ~ghc~ runtime | -| ~-M[size]~ | Maximum heap size | -| ~-H[size]~ | Suggested heap size | -| ~-A[size]~ | Allocation area size used by the garbage collector | +** Pretty Lists Markers +:PROPERTIES: +:CUSTOM_ID: Pretty-Lists-Markers +:END: -Full documentation for the ~ghc~ runtime argumentscan be found [[https://downloads.haskell.org/~ghc/7.8.4/docs/html/users_guide/runtime-control.html][here]]. +When writing, it's common to use ~+,-,*~ to enumerate unordered lists +---especially so in Org-mode wherein they denote structured text. Let's render +them visually as Unicode bullets. -Additional arguments that may be useful include -| ~-S[file]~ | Produces information about “each and every garbage collection” | -| | - Outputs to ~stderr~ by default | +#+begin_src emacs-lisp +;; (x y z) ≈ (existing-item replacement-item positivity-of-preceding-spaces) +(cl-loop for (x y z) in '(("+" "◦" *) + ("-" "•" *) + ("*" "⋆" +)) + do (font-lock-add-keywords 'org-mode + `((,(format "^ %s\\([%s]\\) " z x) + (0 (prog1 () (compose-region (match-beginning 1) (match-end 1) ,y))))))) +#+end_src -:end: +** Fix spelling as you type ---thesaurus & dictionary too! +:PROPERTIES: +:CUSTOM_ID: Fix-spelling-as-you-type-thesaurus-dictionary-too +:END: -#+begin_quote -Unicode doesn't intend to cover things that are achievable with markup, so only -a limited subset of the alphabet is available as subscript; but all is available -as superscript, except ‘q’. +I would like to check spelling on the fly. + + ~C-;~ :: Cycle through corrections for word at point. + + ~M-$~ :: Check and correct spelling of the word at point + + ~M-x ispell-change-dictionary RET TAB~ :: To see what dictionaries are available. -ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ -⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₊ ₋ ₌ ₍ ₎ -ᵃ ᵇ ᶜ ᵈ ᵉ ᶠ ᵍ ʰ ⁱ ʲ ᵏ ˡ ᵐ ⁿ ᵒ ᵖ ʳ ˢ ᵗ ᵘ ᵛ ʷ ˣ ʸ ᶻ -ᴬ ᴮ ᴰ ᴱ ᴳ ᴴ ᴵ ᴶ ᴷ ᴸ ᴹ ᴺ ᴼ ᴾ ᴿ ᵀ ᵁ ⱽ ᵂ -ᵅ ᵝ ᵞ ᵟ ᵋ ᶿ ᶥ ᶲ ᵠ ᵡ ᵦ ᵧ ᵨ ᵩ ᵪ +Install spell-checking application as well as a reliable English +dictionary, [[https://wordnet.princeton.edu/][WordNet]]. + #+begin_src emacs-lisp +(system-packages-ensure "aspell") +(system-packages-ensure "wordnet") + #+end_src -~brew cask install font-symbola~ -⇒ Includes fonts for subscripts; e.g., ₐₙₑₕᵢⱼₖₗₘₙₒₚₜₛ -#+end_quote +~flyspell-prog-mode~ enables spell checking for programming by only considering +comments and strings. -Below are my personal Agda input symbol translations; -e.g., ~\set → 𝒮ℯ𝓉~. Note that we could give a symbol new Agda TeX binding -interactively: ~M-x customize-variable agda-input-user-translations~ then -~INS~ then for key sequence type ~set~ then ~INS~ and for string paste ~𝒮ℯ𝓉~. #+BEGIN_SRC emacs-lisp -(unless noninteractive (add-to-list 'agda-input-user-translations '("set" "𝒮ℯ𝓉"))) +(use-package flyspell + + :hook ((prog-mode . flyspell-prog-mode) + ((org-mode text-mode) . flyspell-mode))) #+END_SRC -Better yet, as a loop: + +Enabling fly-spell for text-mode enables it for org and latex modes since they +derive from text-mode. + +Flyspell needs a spell checking tool, which is not included in Emacs. We +install ~aspell~ spell checker using, say, homebrew via ~brew install aspell~. Note +that Emacs' ~ispell~ is the interface to such a command line spelling utility. +# See available dictionary via ~aspell dicts~. + #+BEGIN_SRC emacs-lisp -(unless noninteractive -(cl-loop for item - in '(;; Arabic ornate parenthesis U+FD3E / U+FD3F - ("(" "﴾") - (")" "﴿") - ("cmd" "⌘") - ;; categorial ;; - ("alg" "𝒜𝓁ℊ") - ("split" "▵") - ("join" "▿") - ("adj" "⊣") - (";;" "﹔") - (";;" "⨾") - (";;" "∘") - ;; logic - ("if" "⇐") - ("onlyif" "⇒") - ;; lattices ;; - ("meet" "⊓") - ("join" "⊔") - ;; tortoise brackets, infix relations - ("((" "〔") - ("))" "〕") - ;; residuals - ("syq" "╳") - ("over" "╱") - ("under" "╲") - ;; Z-quantification range notation ;; - ;; e.g., “∀ x ❙ R • P” ;; - ("|" "❙") - ("with" "❙") - ;; Z relational operators - ("domainrestriction" "◁") - ("domr" "◁") - ("domainantirestriction" "⩤") - ("doma" "⩤") - ("rangerestriction" "▷") - ("ranr" "▷") - ("rangeantirestriction" "⩥") - ("rana" "⩥") - ;; adjunction isomorphism pair ;; - ("floor" "⌊⌋") - ("lower" "⌊⌋") - ("lad" "⌊⌋") - ("ceil" "⌈⌉") - ("raise" "⌈⌉") - ("rad" "⌈⌉") - ;; Replies - ("yes" "✔") - ("no" "❌") - ;; Arrows - ("<=" "⇐") - ;; more (key value) pairs here - ) - do (add-to-list 'agda-input-user-translations item))) +(setq ispell-program-name "/opt/homebrew/bin/aspell") +(setq ispell-dictionary "en_GB") ;; set the default dictionary #+END_SRC -Also some silly stuff: -#+begin_src emacs-lisp -(unless noninteractive -;; Add to the list of translations using “emot” and the given, more specfic, name. -;; Whence, \emot shows all possible emotions. -(cl-loop for emot - in `(;; angry, cry, why-you-no - ("whyme" "ლ(ಠ益ಠ)ლ" "ヽ༼ಢ_ಢ༽ノ☂" "щ(゜ロ゜щ)" "‿︵(ಥ﹏ಥ)‿︵" "ಠ_ಠ" "(╬ ಠ益ಠ)" "・゚(*❦ω❦)*・゚" "(╯°□°)╯︵ ┻━┻") ;; flip the table - ;; confused, disapprove, dead, shrug, awkward - ("what" "「(°ヘ°)" "(ಠ_ಠ)" "(✖╭╮✖)" "¯\\_(ツ)_/¯" "(´°ω°`)" "・✧_✧・") - ;; dance, csi - ("cool" "┏(-_-)┓┏(-_-)┛┗(-_- )┓" - ,(s-collapse-whitespace "•_•) - ( •_•)>⌐■-■ - (⌐■_■)")) - ;; love, pleased, success, yesss, smile, excited, yay - ("smile" "♥‿♥" "(─‿‿─)" "(•̀ᴗ•́)و" "ᕦ( ᴼ ڡ ᴼ )ᕤ" "(งಠ_ಠ)ง" "(。◕‿◕。)" "(◕‿◕)" "( ˃ ヮ˂)" "[ ⇀ ‿ ↼ ]" "٩(⁎❛ᴗ❛⁎)۶" "ᴵ’ᵐ ᵇᵉᵃᵘᵗⁱᶠᵘˡ" "(✿◠‿◠)") - ;; flower high-5 - ("hug" "♡(✿ˇ◡ˇ)人(ˇ◡ˇ✿)♡" "(づ。◕‿◕。)づ" "(づ。◕‿‿‿‿◕。)づ")) - do - (add-to-list 'agda-input-user-translations emot) - (add-to-list 'agda-input-user-translations (cons "emot" (cdr emot))))) -#+end_src -# If you change this setting manually, without using the -# customization buffer, you need to call (agda-input-setup) in -# order for the change to take effect. -Finally let's effect such translations. +[Disabled] Allow spelling support for CamlCase words like “EmacsIsCool”. +#+BEGIN_SRC emacs-lisp :tangle no +(setq ispell-extra-args '("--sug-mode=ultra" + "--run-together" + "--run-together-limit=5" + "--run-together-min=2")) +#+END_SRC + +Let us select a correct spelling merely by clicking on a word +---for the rare days I have a mouse. #+begin_src emacs-lisp -;; activate translations -(unless noninteractive (agda-input-setup)) +(eval-after-load "flyspell" + ' (progn + (define-key flyspell-mouse-map [down-mouse-3] #'flyspell-correct-word) + (define-key flyspell-mouse-map [mouse-3] #'undefined))) #+end_src -Note that the effect of [[http://ergoemacs.org/emacs/emacs_n_unicode.html][Emacs unicode input]] could be approximated using -~abbrev-mode~. +Colour incorrect works; default is an underline. +#+BEGIN_SRC emacs-lisp +(global-font-lock-mode t) +(custom-set-faces '(flyspell-incorrect ((t (:inverse-video t))))) +#+END_SRC -:May_need_to_install_stix_font: -;; install STIX font from Ubuntu store!! -;; (set-fontset-font t 'unicode (font-spec :name "STIX") nil 'append) -:End: +Finally, save to user dictionary without asking: +#+BEGIN_SRC emacs-lisp +(setq ispell-silently-savep t) +#+END_SRC -** Increase/decrease text size -:PROPERTIES: -:CUSTOM_ID: Increase-decrease-text-size -:END: -The ‘usual’ text zoom keys ~C-±~ … +Let's keep track of my personal word set by having it be in my version controlled +.emacs directory. [[http://aspell.net/man-html/Format-of-the-Personal-and-Replacement-Dictionaries.html][Note]] that the default location is ~~/.[i|a]spell.DICT~ for +a specified dictionary ~DICT~. #+BEGIN_SRC emacs-lisp -(global-set-key (kbd "C-+") 'text-scale-increase) -(global-set-key (kbd "C--") 'text-scale-decrease) -;; C-x C-0 restores the default font size +(setq ispell-personal-dictionary "~/.emacs.d/.aspell.en.pws") #+END_SRC -If thou knowst the ELisp, forgive this shadowing of the ~negative-argument~ -… we've still got ~M--~ though. +Nowadays, I very rarely write non-literate programs, but if I do +I'd like to check spelling only in comments/strings. E.g., +#+BEGIN_SRC emacs-lisp +(add-hook 'c-mode-hook 'flyspell-prog-mode) +(add-hook 'emacs-lisp-mode-hook 'flyspell-prog-mode) +#+END_SRC -Curious, this is one of the very first things I did when -began using Emacs; yet, perhaps I would not have done -it if I was simply told the defaults: -+ ~C-x C-=,+~ increases text size -+ ~C-x C--~ decreases test size -+ ~C-x C-0~ restores it to the default size +Use the thesaurus Emacs frontend [[https://github.com/hpdeifel/synosaurus][Synosaurus]] to avoid unwarranted repetition. +#+begin_src emacs-lisp +(use-package synosaurus + :defer 100 + :init (synosaurus-mode) + :config (setq synosaurus-choose-method 'popup) ;; 'ido is default. + (global-set-key (kbd "M-#") 'synosaurus-choose-and-replace)) +#+end_src +The thesaurus is powered by the Wordnet ~wn~ tool, which can be invoked without an +internet connection! +#+begin_src emacs-lisp +;; (shell-command "brew cask install xquartz &") ;; Dependency +;; (shell-command "brew install wordnet &") +#+end_src -So, the above snippet seems to save us of the prefix -~C-x~ and we lose on using ‘=’ for text increase and worse we -need the shift-key to get access to the ‘+’. +Let's use Wordnet as a dictionary via the [[https://github.com/gromnitsky/wordnut][wordnut]] package. +#+BEGIN_SRC emacs-lisp +(use-package wordnut + :defer 100 + :bind ("M-!" . wordnut-lookup-current-word)) -I suppose this is just a habit inherited from using other tools. Fortunately, I -did not inherit the need for the /common user access/ bindings ~C-x~ kill, ~C-c~ copy, -~C-v~ paste, nor ~C-z~ undo of other applications. If you're interested, ~M-x -cua-mode~ to enable [[https://www.gnu.org/software/emacs/manual/html_node/emacs/CUA-Bindings.html][CUA Bindings]]. +;; Use M-& for async shell commands. +#+END_SRC +Use ~M-↑,↓~ to navigate dictionary results, and ~wordnut-search~ for a new search. -** Moving Text Around -:PROPERTIES: -:CUSTOM_ID: Moving-Text-Around -:END: +An alternative to =wordnut= is to use the lightweight ~define-word~ package; which I +think is not ideal since it provides way less information. -This extends Org-mode's ~M-↑,↓~ to other modes, such as when coding. -#+BEGIN_SRC emacs-lisp -;; M-↑,↓ moves line, or marked region; prefix is how many lines. -(use-package move-text - :config (move-text-default-bindings)) -#+END_SRC +:PowerthesaurusCurrentlyNotWorking: +#+BEGIN_SRC emacs-lisp :tangle no +(load "~/dotfiles/.emacs.d/powerthesaurus.el") +(global-set-key (kbd "M-#") 'powerthesaurus-lookup-word-at-point) -** Enabling CamelCase Aware Editing Operations +;; Website currently down ... https://github.com/SavchenkoValeriy/emacs-powerthesaurus/issues/6 +#+END_SRC +:End: +** Touch Typing :Disabled: :PROPERTIES: -:CUSTOM_ID: Enabling-CamelCase-Aware-Editing-Operations +:CUSTOM_ID: Touch-Typing +:header-args: :tangle no :END: -[[https://www.gnu.org/software/emacs/manual/html_node/ccmode/Subword-Movement.html][Subword]] movement lets us treat “EmacsIsAwesome” as three words -─“Emacs”, “Is”, and “Awesome”─ which is desirable since such naming -is common among coders. Now, for example, ~M-f~ moves along each subword. +Use this game to help you learn to spell words that you're having trouble with; +e.g., I have a file ~~/Dropbox/spelling.txt~ with words I have trouble spelling, +which I open then run ~M-x typing-of-emacs~ in order to improve spelling said +words. +#+BEGIN_SRC emacs-lisp :tangle no +;; The Typing Of Emacs, a game. +(use-package typing-of-emacs + :quelpa (typing :fetcher wiki :url "https://www.emacswiki.org/emacs/typing.el")) +#+END_SRC +Practice touch typing using [[https://github.com/hagleitn/speed-type][speed-type]]. #+begin_src emacs-lisp -(global-subword-mode 1) +(use-package speed-type ) #+end_src +Running ~M-x speed-type-region~ on a region of text, or ~M-x speed-type-buffer~ on a +whole buffer, or just ~M-x speed-type-text~ will produce the selected region, buffer, +or random text for practice. The timer begins when the first key is pressed +and stats are shown when the last letter is entered. -** COMMENT Mouse Editing Support -:PROPERTIES: -:CUSTOM_ID: Mouse-Editing-Support -:END: +Other typing resources include: ++ [[https://www.emacswiki.org/emacs/TypingOfEmacs][Typing of Emacs]] ---an Emacs alternative to speed type, possibly more engaging. ++ [[https://alternativeto.net/software/klavaro/][Klavaro]] ---a GUI based yet language-independent typing tutor. + - I'm enjoying this tool in getting started with Arabic typing. ++ [[https://typing.io/][Typing.io]] is a tutor for coders: Lessons are based on open source code, such + some XMonad written in Haskell or Linux written in C. ++ [[https://www.gnu.org/software/gtypist/index.html#downloading][GNU Typist]] ---which is interactive in the terminal, so not ideal in Emacs--, -# :Strange:This_Seems_To_Require_Deprecated_CL:Message_show_at_startup: +To assist in language learning, it may be nice to have an Emacs +[[https://github.com/atykhonov/google-translate][interface]] to Google translate ---e.g., invoke ~google-translate-at-point~. +#+BEGIN_SRC emacs-lisp +(use-package google-translate -Text selected with the mouse is automatically copied to clipboard. -#+begin_src emacs-lisp -(setq mouse-drag-copy-region t) -#+end_src + :config + (global-set-key "\C-ct" 'google-translate-at-point)) +#+END_SRC -** Delete Selection Mode +Select the following then ~C-c t~, +#+begin_quote +Hey buddy, what're you up to? +#+end_quote +Then /detect language/ then /Arabic/ to obtain: +#+begin_quote +مرحباً يا صديقي ، ماذا تفعل؟ +#+end_quote +Neato 😲 + +** Using a Grammar & Style Checker :Disabled: :PROPERTIES: -:CUSTOM_ID: Delete-Selection-Mode +:CUSTOM_ID: Using-a-Grammar-Style-Checker +:header-args: :tangle no :END: -Delete Selection mode lets you treat an Emacs region much like a typical text -selection outside of Emacs: You can replace the active region. We can delete -selected text just by hitting the backspace key. +[ A possibly better alternative is [[https://emacstil.com/til/2022/03/05/setting-up-vale-prose-linter-on-emacs/][Vale]]. ] +Let's install [[https://github.com/mhayashi1120/Emacs-langtool][a grammar and style checker]]. +We get the offline tool from the bottom of the [[https://languagetool.org/][LanguageTool]] website, then relocate it +as follows. #+BEGIN_SRC emacs-lisp -(delete-selection-mode 1) +(use-package langtool + + :custom + (langtool-language-tool-jar + "~/Applications/LanguageTool-4.5/languagetool-commandline.jar")) #+END_SRC -** COMMENT   ~M-n,p~: Word-at-Point Navigation ╱╲ Automatic highlighting current symbol/word -:PROPERTIES: -:CUSTOM_ID: M-n-p-Word-at-Point-Navigation -:END: -Let's mimic the ~C-n,p~ constructs from line to word, so that unoccupied ~M-n,p~ now -serve to take us to the next or previous instance of the word under the -cursor. This is less intrusive than searching ~C-s~ or listing all occurrences ~M-s -o~. Moreover, let's automaticly highlight current symbols, words under the cursour, and cycle -quickly between highlkighted locations with -doc:ahs-forward / [[kbd:M-→]] and -doc:ahs-backward / [[kbd:M-←]]. +Now we can run ~langtool-check~ on the subsequent grammatically incorrect +text ---which is from the LanguageTool website--- which colours errors in red, +when we click on them we get the reason why; then we may invoke +~langtool-correct-buffer~ to quickly use the suggestions to fix each correction, +and finally invoke ~langtool-check-done~ to stop any remaining red colouring. -#+BEGIN_SRC emacs-lisp -;; Default: M-→/← moves to the next/previous instance of the currently highlighted word -;; These are already meaningful commands in Org-mode, so we avoid these key re-bindings in Org-mode; TODO. -(use-package auto-highlight-symbol) -;; :hook ((text-mode . auto-highlight-symbol-mode) -;; (prog-mode . auto-highlight-symbol-mode))) -;; -;; This breaks Org Exports; e.g., -;; C-c C-e h o ⇒ Match data clobbered by buffer modification hooks -#+END_SRC +#+begin_example org +LanguageTool offers spell and grammar checking. Just paste your text here +and click the 'Check Text' button. Click the colored phrases for details +on potential errors. or use this text too see an few of of the problems +that LanguageTool can detecd. What do you thinks of grammar checkers? +Please not that they are not perfect. Style issues get a blue marker: +It's 5 P.M. in the afternoon. The weather was nice on Thursday, 27 June 2017 +--uh oh, that's the wrong date ;-) +#+end_example -Let's also quickly replace the word at point: +By looking around the source code, I can do all three stages smoothly (•̀ᴗ•́)و #+BEGIN_SRC emacs-lisp -(defun my/symbol-replace (replacement) - "Replace all standalone symbols in the buffer matching the one at point." - (interactive (list (read-from-minibuffer "Replacement for thing at point: " nil))) - (save-excursion - (let ((symbol (or (thing-at-point 'symbol) (error "No symbol at point!")))) - (beginning-of-buffer) - ;; (query-replace-regexp symbol replacement) - (replace-regexp (format "\\b%s\\b" (regexp-quote symbol)) replacement)))) +;; Quickly check, correct, then clean up /region/ with M-^ +(eval-after-load 'langtool +(progn +(add-hook 'langtool-error-exists-hook + (lambda () + (langtool-correct-buffer) + (langtool-check-done))) + +(global-set-key "\M-^" + (lambda () + (interactive) + (message "Grammar checking begun ...") + (langtool-check))))) #+END_SRC +The checking command is silent, we added a bit of comforting acknowledgement to the user. -(Unfortunately, as it currently is, there is no universal argument support: ~C-u -2 M-p~ does /not/ take you to the second previous instance of a word ---the prefix -is instead ignored. We can quickly form a work-around with doc:defhydra ---which -gives us temoporary modal modes.) +** Lightweight Prose Proofchecking +:PROPERTIES: +:CUSTOM_ID: Lightweight-Prose-Proofchecking +:END: + +Let's [[https://github.com/bnbeckwith/writegood-mode][write good]]! -Finally, let's use [[kbd:M-n ╱ M-p ╱ M-']] to move to the next, previous, and -replace the word at point /but also/ to give us a meanu to repeat any of these -actions. #+BEGIN_SRC emacs-lisp -(defmacro my/make-navigation-hydra (initial-action) - `(defhydra word-navigation - (:body-pre (,initial-action)) "Word-at-point Navigation" - ("n" ahs-forward "Next instance") - ("p" smartscan-symbol-go-backward "Previous instance") - ("r" my/symbol-replace "Replace all occurances") - ("s" ahs-display-stat "Stats"))) +(use-package writegood-mode + ;; Load this whenver I'm composing prose. + :hook (text-mode org-mode) + ;; Don't show me the “Wg” marker in the mode line -;; (bind-key* str func) ≈ (global-set-key (kbd str) func) -(bind-key* "M-n" (my/make-navigation-hydra ahs-forward)) -(bind-key* "M-p" (my/make-navigation-hydra ahs-backward)) -(bind-key* "M-'" (my/make-navigation-hydra my/symbol-replace)) + :defer 100 + + ;; Some additional weasel words. + :config + (--map (push it writegood-weasel-words) + '("some" "simple" "simply" "easy" "often" "easily" "probably" + "clearly" ;; Is the premise undeniably true? + "experience shows" ;; Whose? What kind? How does it do so? + "may have" ;; It may also have not! + "it turns out that"))) ;; How does it turn out so? + ;; ↯ What is the evidence of highighted phrase? ↯ #+END_SRC -An alternative is the [[https://github.com/mickeynp/smart-scan][smart-scan]] library ---which does the same thing, but does -not provide the highlighting occurances at point. -# The default symbol replacement is [[https://github.com/mickeynp/smart-scan/issues/23][over-zealous]] in that it replaces sub-terms -# occurring as parts of larger words. Let's do something about that. -** Letter-based Navigation -:PROPERTIES: -:CUSTOM_ID: Letter-based-Navigation -:END: -At a glance of possible positions, across windows, -and a key to jump there is a feature provided to us by [[https://github.com/winterTTr/ace-jump-mode/wiki/AceJump-FAQ][ace-jump]] ----here is an [[https://www.youtube.com/watch?feature=player_embedded&v=UZkpmegySnc#!][emacs-rocks 2-minute video]]. +Inspired by Matt Might's [[http://matt.might.net/articles/shell-scripts-for-passive-voice-weasel-words-duplicates/][3 shell scripts to improve your writing, or +"My Ph.D. advisor rewrote himself in bash"]], this Emacs interface +emphasises, via underline, the following weaknesses in writing ---so +that I can fix them or decide that they are appropriate for the +scenario. -For example, =C-c SPC m= greys our all windows and places a red -letter at the start of any word that begins with /m/, then I may -press a letter to jump to the associated position in the -associated window. Using ~C-u C-c SPC~ and ~C-u C-u C-c SPC~ let -me jump to any character or to any visible line. +Sentences that cut out the following problems may become stronger +---by being more terse or precise. -➩ Super simple use case: Fix your eyes on an occurence of a word, then ~C-c SPC~ - to quickly jump to it so as to edit the sentence in which it occurs. - - It's like ~C-s~ but more lightweight. ++ Weasel Words :: + Phrases that sound good without conveying information; + such as vague precision or subjective phrases. -#+begin_src emacs-lisp -(use-package ace-jump-mode - :defer t - :config (bind-key* "C-c SPC" 'ace-jump-mode)) + E.g., /a number of, surprisingly, very close/. -;; See ace-jump issues to configure for use of home row keys. -#+end_src + It's okay not to have exact details, but rather than “I don't know” + explain why not and what the next steps will be. -There is a newer and somewhat more powerful package, [[https://github.com/abo-abo/avy][avy]], which accompishes the -same goal. It uses a tree style to jumipng: Locations are given two letter -combinations, one presses one letter to jump to a group of text, then another -letter to jump somewhere in that grouping. I prefer ace-jump since it greys -everthing out, whereas avy surrounds jump locations with a box. -Here is an [[https://www.youtube.com/watch?v=zar4GsOBU0g][emacs-doom 6-minute video]] for avy. ++ Passive Voice :: + Phrases wherein interest is in the object experiencing an action, + rather than the subject that performs the action. -There is also [[https://github.com/tam17aki/ace-isearch][ace-isearch]] for bridinging different navgiational methods ---one -begins incremental search, ~s-f~, then according to a pause and length of input, -one of the navgiational methods, such as isearch or avy or helm-swoop, will be -begun. I'm okay with using ~C-s~ for helm-swoop and ~C-c SPC~ for ace-jump, and -still have ~s-f~ for incremental search, which I hardly use. + - Bad: The house /was built by/ my father. + - Good: My father /built/ this house. -*What is bind-keys**? + Likewise, including relevant or explanatory information as in “X + guarantees Y” is an improvement over “Y is guaranteed”. -Major modes provide specfic use and so their bindings always take precedence -over global bindings ---e.g., the major mode binding may do what the global does -but with extra mode-specfic behaviour, such as indentation. Other times, a major -mode's binding simply uses the same key presses with completely unrelated -behaviour. If we want to avoid having our global keybindings shadowed by a -major mode, we may use the ~bind-key*~ /macro/ of ~use-package~, or the ~bind-keys*~ -/macro/ when there are multiple keys; these are macros, not clauses. ---These -essentially creates a dedicated minor mode behind the scenes, which saves us the -work of [[https://emacs.stackexchange.com/a/358/10352][doing it ourselves]]. + Sometimes the subject really is irrelevant, such as + “We did X” whereas “X happened” suffices. -| | ~(bind-keys* (k₁ . f₁) … (kₙ . fₙ))~ | -| ≈ | These keybindings override all minor modes that use keys =kᵢ=. | + 👍 If the relevant subject is unclear and, also, the text reads + better in the active, then change a phrase. -Of course we can also use it without the asterisk; e.g.: -#+begin_src emacs-lisp -;; C-x o ⇒ Switch to the other window -;; C-x O ⇒ Switch back to the previous window -(bind-key "C-x O" (lambda () (interactive) (other-window -1))) -#+end_src ++ Duplicated Words :: Occurrences of, say, “the the”. + + Harder to catch manually, but easier mechanically ;-) -** COMMENT   =C-c e n,p=: Taking a tour of one's edits +** Placeholder Text ---For Learning & Experimenting :PROPERTIES: -:CUSTOM_ID: C-c-e-n-p-Taking-a-tour-of-one's-edits +:CUSTOM_ID: Placeholder-Text-For-Learning-Experimenting :END: -This package allows us to move around the edit points of a buffer /without/ -actually undoing anything. We even obtain a brief description of what happend at -each edit point. This seems useful for when I get interrupted or lose my train -of thought: Just press [[kbd:C-c e p]] to see what I did recently and where ---the -“e” is for “e”dit. -#+BEGIN_SRC emacs-lisp -;; Give me a description of the change made at a particular stop. -(use-package goto-chg - :defer t - :custom (glc-default-span 0)) +When learning about Emacs formatting commands, such as zap-to-char ~M-z~ +or transpose ~M-t~, it's best to have filler text ---even better when +it's automatically generated instead of typing it out ourselves. The +following will give us a series of commands ~lorem-ipsum-insert-⋯~ for +inserting lists, sentences, paragraphs and using a prefix argument, +with ~C-u~, we can request to generate any number of them. -(my/defhydra "C-c e" "Look at them edits!" bus - :\ ("p" goto-last-change "Goto nᵗʰ last change") - ("n" goto-last-change-reverse "Goto more recent change")) +#+BEGIN_SRC emacs-lisp +(use-package lorem-ipsum ) #+END_SRC -Compare this with ~C-x u~, or ~undo-tree-visualise~, wherein undos are actually performed. - -Notice, as a hydra, I can use ~C-c e~ followed by any combination of ~p~ and ~n~ to -navigate my recent edits /without/ having to supply the prefix each time. +‘Lorem’ is not a word itself, but it comes from the Latin ‘Dolorem Ipsum’ +which means “pain in and of itself”. -** visual-regexp -:PROPERTIES: -:CUSTOM_ID: visual-regexp -:END: -#+begin_src emacs-lisp -;; While constructing the regexp in the minibuffer, get live visual feedback for the (group) matches. -;; E.g., try: M-% use-\(.+?\) \(.+\)\b ENTER woah \1 and \2 -;; -;; C-u M-% do to regexp replace, without querying. -(use-package visual-regexp - :config (define-key global-map (kbd "M-%") - (lambda (&optional prefix) (interactive "P") (call-interactively (if prefix #'vr/replace #'vr/query-replace))))) -#+end_src +See this [[https://github.com/alhassy/emacs.d/blob/master/CheatSheet.pdf][Emacs Cheat Sheet]] to try out the textual navigation and formatting +bindings on lorem ipsum, gibberish text. -** COMMENT LaTeX ⇐ Org-Mode -CLOSED: [2021-11-02 Tue 22:18] +** Some text to make us smile :PROPERTIES: -:CUSTOM_ID: Org-Mode-PDF-HTML +:CUSTOM_ID: Some-text-to-make-us-smile :END: -In this section we consider the Org-mode export for PDFs (LaTeX). -For example, we account for LaTeX citations. +The [[https://github.com/davep/dad-joke.el][dad-joke]] queries [[https://icanhazdadjoke.com][https://icanhazdadjoke.com]] to bring us some funny. +#+begin_src emacs-lisp +(use-package dad-joke -# Installing LaTex -# $ brew install --cask mactex -# $ eval "$(/usr/libexec/path_helper)" -# $ type pdflatex # => pdflatex is /Library/TeX/texbin/pdflatex -# -# The ~minted~ environment can't appear as an argument to another command; a -# [[https://tex.stackexchange.com/questions/102416/error-when-compiling-a-minted-listings-inside-a-memoir-subfloat][work-around]] is to use a ‘box’. Learn more about LaTeX boxes [[https://latexref.xyz/Boxes.html][here]]. + :config (defun dad-joke () (interactive) (insert (dad-joke-get)))) +#+end_src +For example, ~M-x dad-joke~ now inserts: +#+begin_quote org +What are the strongest days of the week? Saturday and Sunday...the rest are +weekdays. +#+end_quote -*** Get LaTeX: +** Unicode Input via Agda Input :PROPERTIES: -:CUSTOM_ID: Get-LaTeX +:CUSTOM_ID: Unicode-Input-via-Agda-Input :END: +:agda2-include-dirs-Issue: +#+BEGIN_SRC emacs-lisp +;; (load (shell-command-to-string "agda-mode locate")) +;; +;; Seeing: One way to avoid seeing this warning is to make sure that agda2-include-dirs is not bound. +; (makunbound 'agda2-include-dirs) +#+END_SRC +:End: -# +begin_src shell -# time brew cask install mactex-no-gui -# +end_src +[[https://mazzo.li/posts/AgdaSort.html][Agda]] is one of my favourite languages, it's like Haskell on steroids. Let's set +it up for the main sake of its Unicode input ---you may do likewise using TeX +input. ( [[https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/][The Absolute Minimum Every Software Developer Absolutely, Positively +Must Know About Unicode and Character Sets (No Excuses!)]] ) + +/Agda input mode makes it extremely easy to use unicode in documents, something I/ +/strongly prefer to do. When I can use symbols directly, instead of (for/ +/instance) LaTeX commands, it makes my plaintext far more readable./ --- [[https://github.com/armkeh/dotfiles/tree/master/emacs][Armkeh +.emacs config]] #+begin_src emacs-lisp -(system-packages-ensure "mactex") +(system-packages-ensure "agda") #+end_src - - This is a redistribution of TeX Live specifically for macOS. - - We get the 4GB version since it has [[https://tex.stackexchange.com/a/1041/69371][everything]] and so - do not need to worry about missing style files. - - This took about 12 minutes on my machine. +#+begin_details To use the Agda standard library by default + #+BEGIN_SRC shell :tangle no + mkdir -p ~/.agda + echo /usr/local/lib/agda/standard-library.agda-lib >>~/.agda/libraries + echo standard-library >>~/.agda/defaults + #+END_SRC + Invoke ~brew info agda~ to get these instructions and the version of Agda just + installed. +#+end_details - Restart Emacs, enter =$e^{i \cdot \pi} + 1 = 0$= then press kbd:C-c_C-x_C-l to - have it rendered inline. +#+begin_details Get font support for subscripts, if need be + - Download and unzip the [[https://fontlibrary.org/en/font/symbola][symbola]] font + - kbd:CMD_+_SPC ⇒ =font book= ⇒ CMD+O ⇒ select the symbola directory you just + unzipped - + *Minted:* Get tool for colourful code snippets for LaTeX ---see “minted” in - the main article. + (Note: In the before time, you could brew install this font.) +#+end_details - #+begin_src emacs-lisp -(system-packages-ensure "pygments") +Executing ~agda-mode setup~ appends the following text to the ~.emacs~ file. +Let's put it here ourselves. +#+begin_src emacs-lisp +(unless noninteractive + (load-file (let ((coding-system-for-read 'utf-8)) + (shell-command-to-string "agda-mode locate")))) #+end_src - + *Not anymore:* Get a neato PDF presentation console: =brew install pdfpc= - - With =pdfpc myfile.pdf= you get a nice timer and multiple views - of the current slide and upcoming slides ---with support for multiple monitors. - - Install ScreenBrush, from the Apple Store, for easily drawing/annotating my - screen ---e.g., when I'm giving a virtual lecture to my students. - - An alternative is =brew install mupdf= then =mupdf-gl myfile.pdf= and press - =f= for fullscreen then =a= for adding/adorning drawings ---it was too - rough to use live. - - Finally, within Emacs: =M-x pdf-tools-install= - -:Possibly_breaking_toc-org: -#+BEGIN_SRC emacs-lisp :tangle no -;; Use 3 headlines of export, which is the default -;; (setq org-export-headline-levels 4) - -;; no numbers by default at export -;; (setq org-export-with-section-numbers nil) -#+END_SRC -:End: -*** Working with Citations :Disabled:Not_Used: -:PROPERTIES: -:CUSTOM_ID: Working-with-Citations -:END: - -# TODO Fix me, breaking Github Actions test setup - - [[https://github.com/jkitchin/org-ref][An exquisite system]] for handling references. +I almost always want the ~agda-mode~ input method ---it's like the TeX method, but +better. +#+BEGIN_SRC emacs-lisp - The following entity will display useful data - when the mouse hovers over it (•̀ᴗ•́)و If you click on it, then you're - in for a lot of super neat stuff, such as searching for the pdf online! -cite:agda_overview ( In HTML export, the citation doesn't link anywhere. ) +;; TODO: Maybe don't bother installing Agda, and just get agda-input.el +;; from: https://github.com/agda/agda/blob/master/src/data/emacs-mode/agda-input.el +;; then loading that! +(url-copy-file "https://raw.githubusercontent.com/agda/agda/master/src/data/emacs-mode/agda-input.el" "~/.emacs.d/elpa/agda-input.el" :ok-if-already-exists) +(load-file "~/.emacs.d/elpa/agda-input.el") - #+BEGIN_SRC emacs-lisp :tangle no +;; MA: This results in "Package cl is deprecated" !? (unless noninteractive + (use-package agda-input + :ensure nil ;; I have it locally. + :demand t + :hook ((text-mode prog-mode) . (lambda () (set-input-method "Agda"))) + :custom (default-input-method "Agda"))) + ;; Now C-\ or M-x toggle-input-method turn it on and offers -(use-package org-ref - :custom ;; Files to look at when no “╲bibliography{⋯}” is not present in a file. - ;; Most useful for non-LaTeX files. - (reftex-default-bibliography '("~/thesis-proposal/papers/References.bib")) - (bibtex-completion-bibliography (car reftex-default-bibliography)) - (org-ref-default-bibliography reftex-default-bibliography)) -;; Quick BibTeX references, sometimes. -(use-package helm-bibtex) -(use-package biblio) ) +;; TODO add a hook that when the input method becomes Agda, just don't bother showing me in the modeline. +;; E.g., "Π" when using unicode input with Agda +;; Useful to have in the modeline, say when typing in Arabic. +;; (add-variable-watcher +;; 'current-input-method +;; (lambda (_ newvalue 'set _) +;; (setq current-input-method-title +;; (if (equal newvalue "Agda") nil newvalue)))) #+END_SRC - Execute ~M-x helm-bibtex~ or =C-c ]= and, say, enter =emacs= and you will be - presented with all the entries in the bib database that mention ‘emacs’. Super - cool stuff. Moreover, if no such entries exist, then we can look some up - using the interface! - - Read the manual [[https://github.com/jkitchin/org-ref/blob/master/org-ref.org][online]] or better yet as an org-file with ~M-x org-ref-help~. +:agda_Command_line_arguments: + "+RTS -H4.5G -M4.5G -K256M -S/tmp/AgdaRTS.log -A1G -RTS -i ." - This is an Org-mode application since the citations have tooltips and export - nicely to LaTeX & HTML via the Org-mode exporter. +Wolfram Kahl has recommended the following settings. -*** Bibliography & Coloured LaTeX using Minted -:PROPERTIES: -:CUSTOM_ID: Bibliography-Coloured-LaTeX-using-Minted -:END: +#+begin_src emacs-lisp +;;(setq agda2-program-args (quote ("RTS" "-M4G" "-H4G" "-A128M" "-RTS"))) +#+end_src -Execute the following for bibliography references as well as minted Org-mode -uses the Minted package for source code highlighting in PDF/LaTeX ---which in -turn requires the pygmentize system tool. -#+BEGIN_SRC emacs-lisp -(setq org-latex-listings 'minted - org-latex-packages-alist '(("" "minted")) - org-latex-pdf-process - '("pdflatex -shell-escape -output-directory %o %f" - "biber %b" - "pdflatex -shell-escape -output-directory %o %f" - "pdflatex -shell-escape -output-directory %o %f")) -#+END_SRC +These arguments specify +| ~+RTS~, ~-RTS~ | Flags between these are arguments to the ~ghc~ runtime | +| ~-M[size]~ | Maximum heap size | +| ~-H[size]~ | Suggested heap size | +| ~-A[size]~ | Allocation area size used by the garbage collector | -For faster pdf generation, possibly with errors, consider invoking: -#+begin_example emacs-lisp -(setq org-latex-pdf-process - '("pdflatex -interaction nonstopmode -output-directory %o %f")) -#+end_example +Full documentation for the ~ghc~ runtime argumentscan be found [[https://downloads.haskell.org/~ghc/7.8.4/docs/html/users_guide/runtime-control.html][here]]. -By default, Org exports LaTeX using the ~nonstopmode~ option, which tries -its best to produce a PDF ---which ignores typesetting errors altogether, -which is not necessary ideal when using LaTeX. +Additional arguments that may be useful include +| ~-S[file]~ | Produces information about “each and every garbage collection” | +| | - Outputs to ~stderr~ by default | -*** COMMENT Excellent PDF Viewer -:PROPERTIES: -:CUSTOM_ID: Excellent-PDF-Viewer -:END: +:end: +#+begin_quote +Unicode doesn't intend to cover things that are achievable with markup, so only +a limited subset of the alphabet is available as subscript; but all is available +as superscript, except ‘q’. -Need to (re)build the epdfinfo program, do it now ? (y or n) n +ₐₑₕᵢⱼₖₗₘₙₒₚᵣₛₜᵤᵥₓ +⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹ ⁺ ⁻ ⁼ ⁽ ⁾ ₀ ₁ ₂ ₃ ₄ ₅ ₆ ₇ ₈ ₉ ₊ ₋ ₌ ₍ ₎ +ᵃ ᵇ ᶜ ᵈ ᵉ ᶠ ᵍ ʰ ⁱ ʲ ᵏ ˡ ᵐ ⁿ ᵒ ᵖ ʳ ˢ ᵗ ᵘ ᵛ ʷ ˣ ʸ ᶻ +ᴬ ᴮ ᴰ ᴱ ᴳ ᴴ ᴵ ᴶ ᴷ ᴸ ᴹ ᴺ ᴼ ᴾ ᴿ ᵀ ᵁ ⱽ ᵂ +ᵅ ᵝ ᵞ ᵟ ᵋ ᶿ ᶥ ᶲ ᵠ ᵡ ᵦ ᵧ ᵨ ᵩ ᵪ --------------------------------------------------------------------------------- +~brew cask install font-symbola~ +⇒ Includes fonts for subscripts; e.g., ₐₙₑₕᵢⱼₖₗₘₙₒₚₜₛ +#+end_quote -Let's install the [[https://github.com/politza/pdf-tools][pdf-tools]] library for viewing PDFs in Emacs. +Below are my personal Agda input symbol translations; +e.g., ~\set → 𝒮ℯ𝓉~. Note that we could give a symbol new Agda TeX binding +interactively: ~M-x customize-variable agda-input-user-translations~ then +~INS~ then for key sequence type ~set~ then ~INS~ and for string paste ~𝒮ℯ𝓉~. #+BEGIN_SRC emacs-lisp -(use-package pdf-tools - ; :init (system-packages-ensure "pdf-tools") - :custom (pdf-tools-handle-upgrades nil) - (pdf-info-epdfinfo-program "/usr/local/bin/epdfinfo") - :config (pdf-tools-install)) - -;; Now PDFs opened in Emacs are in pdfview-mode. +(unless noninteractive (add-to-list 'agda-input-user-translations '("set" "𝒮ℯ𝓉"))) #+END_SRC - -Besides the expected PDF viewing utilities, such as search, annotation, and continuous scrolling; -with a simple mouse right-click, we can even select a ‘midnight’ rendering mode which may be -easier on the eyes. For more, see the brief [[https://www.dailymotion.com/video/x2bc1is][pdf-tools-tourdeforce]] demo. -** HTML ⇐ Org-mode -CLOSED: [2021-11-02 Tue 22:18] -:PROPERTIES: -:CUSTOM_ID: HTML-Org-mode -:END: - -In this section we consider the Org-mode exporters for PDFs and -HTMLs. For example, we account for LaTeX citations and reliable HTML -anchors. - +Better yet, as a loop: #+BEGIN_SRC emacs-lisp -(use-package htmlize :defer t) -;; Main use: Org produced htmls are coloured. -;; Can be used to export a file into a coloured html. +(unless noninteractive +(cl-loop for item + in '(;; Arabic ornate parenthesis U+FD3E / U+FD3F + ("(" "﴾") + (")" "﴿") + ("cmd" "⌘") + ;; categorial ;; + ("alg" "𝒜𝓁ℊ") + ("split" "▵") + ("join" "▿") + ("adj" "⊣") + (";;" "﹔") + (";;" "⨾") + (";;" "∘") + ;; logic + ("if" "⇐") + ("onlyif" "⇒") + ;; lattices ;; + ("meet" "⊓") + ("join" "⊔") + ;; tortoise brackets, infix relations + ("((" "〔") + ("))" "〕") + ;; residuals + ("syq" "╳") + ("over" "╱") + ("under" "╲") + ;; Z-quantification range notation ;; + ;; e.g., “∀ x ❙ R • P” ;; + ("|" "❙") + ("with" "❙") + ;; Z relational operators + ("domainrestriction" "◁") + ("domr" "◁") + ("domainantirestriction" "⩤") + ("doma" "⩤") + ("rangerestriction" "▷") + ("ranr" "▷") + ("rangeantirestriction" "⩥") + ("rana" "⩥") + ;; adjunction isomorphism pair ;; + ("floor" "⌊⌋") + ("lower" "⌊⌋") + ("lad" "⌊⌋") + ("ceil" "⌈⌉") + ("raise" "⌈⌉") + ("rad" "⌈⌉") + ;; Replies + ("yes" "✔") + ("no" "❌") + ;; Arrows + ("<=" "⇐") + ;; more (key value) pairs here + ) + do (add-to-list 'agda-input-user-translations item))) #+END_SRC +Also some silly stuff: +#+begin_src emacs-lisp +(unless noninteractive +;; Add to the list of translations using “emot” and the given, more specfic, name. +;; Whence, \emot shows all possible emotions. +(cl-loop for emot + in `(;; angry, cry, why-you-no + ("whyme" "ლ(ಠ益ಠ)ლ" "ヽ༼ಢ_ಢ༽ノ☂" "щ(゜ロ゜щ)" "‿︵(ಥ﹏ಥ)‿︵" "ಠ_ಠ" "(╬ ಠ益ಠ)" "・゚(*❦ω❦)*・゚" "(╯°□°)╯︵ ┻━┻") ;; flip the table + ;; confused, disapprove, dead, shrug, awkward + ("what" "「(°ヘ°)" "(ಠ_ಠ)" "(✖╭╮✖)" "¯\\_(ツ)_/¯" "(´°ω°`)" "・✧_✧・") + ;; dance, csi + ("cool" "┏(-_-)┓┏(-_-)┛┗(-_- )┓" + ,(s-collapse-whitespace "•_•) + ( •_•)>⌐■-■ + (⌐■_■)")) + ;; love, pleased, success, yesss, smile, excited, yay + ("smile" "♥‿♥" "(─‿‿─)" "(•̀ᴗ•́)و" "ᕦ( ᴼ ڡ ᴼ )ᕤ" "(งಠ_ಠ)ง" "(。◕‿◕。)" "(◕‿◕)" "( ˃ ヮ˂)" "[ ⇀ ‿ ↼ ]" "٩(⁎❛ᴗ❛⁎)۶" "ᴵ’ᵐ ᵇᵉᵃᵘᵗⁱᶠᵘˡ" "(✿◠‿◠)") + ;; flower high-5 + ("hug" "♡(✿ˇ◡ˇ)人(ˇ◡ˇ✿)♡" "(づ。◕‿◕。)づ" "(づ。◕‿‿‿‿◕。)づ")) + do + (add-to-list 'agda-input-user-translations emot) + (add-to-list 'agda-input-user-translations (cons "emot" (cdr emot))))) +#+end_src -*** Ensuring Useful HTML Anchors -:PROPERTIES: -:CUSTOM_ID: Ensuring-Useful-HTML-Anchors -:END: - -Upon HTML export, each tree heading is assigned an ID to be used for hyperlinks. -Default IDs are something like ~org1957a9d~, which does not endure the test of time: -Re-export will produce a different id. Here's a rough snippet to generate -IDs from headings, by replacing spaces with hyphens, for headings without IDs. - -#+BEGIN_SRC emacs-lisp -(defun my/ensure-headline-ids (&rest _) - "Org trees without a - -All non-alphanumeric characters are cleverly replaced with ‘-’. - -If multiple trees end-up with the same id property, issue a -message and undo any property insertion thus far. - -E.g., ↯ We'll go on a ∀∃⇅ adventure - ↦ We'll-go-on-a-adventure -" - (interactive) - (let ((ids)) - (org-map-entries - (lambda () - (org-with-point-at (point) - (let ((id (org-entry-get nil "CUSTOM_ID"))) - (unless id - (thread-last (nth 4 (org-heading-components)) - (s-replace-regexp "[^[:alnum:]']" "-") - (s-replace-regexp "-+" "-") - (s-chop-prefix "-") - (s-chop-suffix "-") - (setq id)) - (if (not (member id ids)) - (push id ids) - (message-box "Oh no, a repeated id!\n\n\t%s" id) - (undo) - (setq quit-flag t)) - (org-entry-put nil "CUSTOM_ID" id)))))))) - -;; Whenever html & md export happens, ensure we have headline ids. -(advice-add 'org-html-export-to-html :before 'my/ensure-headline-ids) -(advice-add 'org-md-export-to-markdown :before 'my/ensure-headline-ids) -#+END_SRC - -One may then use ~[[#my-custom-id]]~ to link to the entry with ~CUSTOM_ID~ -property ~my-custom-id~. - -Interestingly, ~org-set-property~, ~C-c C-x p~, lets us insert a property -from a selection of available ones, then we'll be prompted for a value -for it from a list of values you've used elsewhere. This is useful for -remaining consistent for when trees share similar properties. - -*** Clickable Headlines -:PROPERTIES: -:CUSTOM_ID: Clickable-Headlines -:END: - -By default, HTML export generates ID's to headlines so they may be referenced -to, but there is no convenient way to get at them to refer to a particular -heading. The following spell fixes this issue: Headlines are now clickable, -resulting in a link to the headline itself. - +# If you change this setting manually, without using the +# customization buffer, you need to call (agda-input-setup) in +# order for the change to take effect. +Finally let's effect such translations. #+begin_src emacs-lisp -;; Src: https://writepermission.com/org-blogging-clickable-headlines.html -(setq org-html-format-headline-function - (lambda (todo todo-type priority text tags info) - "Format a headline with a link to itself." - (let* ((headline (get-text-property 0 :parent text)) - (id (or (org-element-property :CUSTOM_ID headline) - (ignore-errors (org-export-get-reference headline info)) - (org-element-property :ID headline))) - (link (if id - (format "%s" id text) - text))) - (org-html-format-headline-default-function todo todo-type priority link tags info)))) +;; activate translations +(unless noninteractive (agda-input-setup)) #+end_src -*Warning:* The header cannot already be a link! Otherwise you get cyrptic and -unhelpful error =(wrong-type-argument plistp :section-number)=; which then -pollutes the current Emacs session resulting in stange =nil= errors after =C-x C-s=, -thereby forcing a full Emacs restart. Instead, you need at least one portion of -each heading to be not a link. +Note that the effect of [[http://ergoemacs.org/emacs/emacs_n_unicode.html][Emacs unicode input]] could be approximated using +~abbrev-mode~. -1. Need to have a custom id declared. - #+BEGIN_SRC org :tangle no - :PROPERTIES: - :CUSTOM_ID: my-header - :END: -#+END_SRC -2. Failing headers: =* [[link]]= nor =* ~code~= nor =* $math$=. - - Any non-link text /before/ it will work: =ok [[link]]=. - * Using Unicode non-breaking space ‘ ’ is ok. - - Text /only after/ the link is insufficient. -*** HTML “Folded Drawers” +:May_need_to_install_stix_font: +;; install STIX font from Ubuntu store!! +;; (set-fontset-font t 'unicode (font-spec :name "STIX") nil 'append) +:End: + +** Increase/decrease text size :PROPERTIES: -:CUSTOM_ID: HTML-Folded-Drawers +:CUSTOM_ID: Increase-decrease-text-size :END: - +The ‘usual’ text zoom keys ~C-±~ … #+BEGIN_SRC emacs-lisp -(defun my/org-drawer-format (name contents) - "Export to HTML the drawers named with prefix ‘fold_’, ignoring case. - -The resulting drawer is a ‘code-details’ and so appears folded; -the user clicks it to see the information therein. -Henceforth, these are called ‘fold drawers’. +(global-set-key (kbd "C-+") 'text-scale-increase) +(global-set-key (kbd "C--") 'text-scale-decrease) +;; C-x C-0 restores the default font size +#+END_SRC -Drawers without such a prefix may be nonetheless exported if their -body contains ‘:export: t’ ---this switch does not appear in the output. -Thus, we are biased to generally not exporting non-fold drawers. +If thou knowst the ELisp, forgive this shadowing of the ~negative-argument~ +… we've still got ~M--~ though. -One may suspend export of fold drawers by having ‘:export: nil’ -in their body definition. +Curious, this is one of the very first things I did when +began using Emacs; yet, perhaps I would not have done +it if I was simply told the defaults: ++ ~C-x C-=,+~ increases text size ++ ~C-x C--~ decreases test size ++ ~C-x C-0~ restores it to the default size -Fold drawers naturally come with a title. -Either it is specfied in the drawer body by ‘:title: ⋯’, -or otherwise the drawer's name is used with all underscores replaced -by spaces. -" - (let* ((contents′ (replace-regexp-in-string ":export:.*\n?" "" contents)) - (fold? (s-prefix? "fold_" name 'ignore-case)) - (export? (string-match ":export:\s+t" contents)) - (not-export? (string-match ":export:\s+nil" contents)) - (title′ (and (string-match ":title:\\(.*\\)\n" contents) - (match-string 1 contents)))) +So, the above snippet seems to save us of the prefix +~C-x~ and we lose on using ‘=’ for text increase and worse we +need the shift-key to get access to the ‘+’. - ;; Ensure we have a title. - (unless title′ (setq title′ (s-join " " (cdr (s-split "_" name))))) +I suppose this is just a habit inherited from using other tools. Fortunately, I +did not inherit the need for the /common user access/ bindings ~C-x~ kill, ~C-c~ copy, +~C-v~ paste, nor ~C-z~ undo of other applications. If you're interested, ~M-x +cua-mode~ to enable [[https://www.gnu.org/software/emacs/manual/html_node/emacs/CUA-Bindings.html][CUA Bindings]]. - ;; Output - (cond - ((and export? (not fold?)) contents′) - (not-export? nil) - (fold? - (thread-last contents′ - (replace-regexp-in-string ":title:.*\n" "") - (format "
- %s - %s
" title′)))))) +** Moving Text Around +:PROPERTIES: +:CUSTOM_ID: Moving-Text-Around +:END: -(setq org-html-format-drawer-function 'my/org-drawer-format) +This extends Org-mode's ~M-↑,↓~ to other modes, such as when coding. +#+BEGIN_SRC emacs-lisp +;; M-↑,↓ moves line, or marked region; prefix is how many lines. +(use-package move-text + :config (move-text-default-bindings)) #+END_SRC -With the following invocations we only see the odd indexed ‘hello’s, where the -latter two are folded up. -#+BEGIN_SRC org :tangle no -:this-drawer-is-exported: -:export: t -hello 1 -:End: - -:this-drawer-is-NOT-exported: -hello 2 -:End: - -:fold_This_drawer_has_a_title_in_the_body: -:title: I am the drawer title 0 - -hello 3 -:End: - -:fold_This_drawer_is_NOT_exported: -:title: Why are we here? -:export: nil +** Enabling CamelCase Aware Editing Operations +:PROPERTIES: +:CUSTOM_ID: Enabling-CamelCase-Aware-Editing-Operations +:END: -hello 4 -:End: +[[https://www.gnu.org/software/emacs/manual/html_node/ccmode/Subword-Movement.html][Subword]] movement lets us treat “EmacsIsAwesome” as three words +─“Emacs”, “Is”, and “Awesome”─ which is desirable since such naming +is common among coders. Now, for example, ~M-f~ moves along each subword. -:fold_I_am_the_drawer_title_1: +#+begin_src emacs-lisp +(global-subword-mode 1) +#+end_src -hello 5 -:End: -#+END_SRC +** Mouse Editing Support :Disabled: +:PROPERTIES: +:CUSTOM_ID: Mouse-Editing-Support +:header-args: :tangle no +:END: -I doubt I could show an example in the Github README, since no HTML export is -happening using my setup. In case you're reading this on my blog, which has -exported HTML. Here's the example: -:fold_hello_world: -:title: ¡Hola! Buenas tardes, Amigo -Hey bud, hope you're enjoying this read ^_^ -:End: +# :Strange:This_Seems_To_Require_Deprecated_CL:Message_show_at_startup: -Now that I've written this, I'm thinking it may have been preferably to use an org-block…? +Text selected with the mouse is automatically copied to clipboard. +#+begin_src emacs-lisp +(setq mouse-drag-copy-region t) +#+end_src -*** Diagrams with Mermaid ---Not Reccommended +** Delete Selection Mode :PROPERTIES: -:CUSTOM_ID: Diagrams-with-Mermaid-Not-Reccommended +:CUSTOM_ID: Delete-Selection-Mode :END: -Let's try out an alternative to PlantUML ---covered below in §[[Workflow States]]. - -First, let's get the tool. -#+BEGIN_SRC shell :tangle no -npm install mermaid.cli -sudo git clone git@github.com:arnm/mermaid-layer.git ~/.emacs.d/private/mermaid -#+END_SRC +Delete Selection mode lets you treat an Emacs region much like a typical text +selection outside of Emacs: You can replace the active region. We can delete +selected text just by hitting the backspace key. -Then, let's get the associated ~mermaid~ package. #+BEGIN_SRC emacs-lisp -(use-package ob-mermaid - :custom ob-mermaid-cli-path "~/node_modules/.bin/mmdc") +(delete-selection-mode 1) #+END_SRC - -Then, =C-c C-c= on the following: -#+BEGIN_SRC mermaid :file test.png :theme neutral :background-color green :tangle no :results replace :eval never -sequenceDiagram -A-->B: Works! -#+END_SRC - -#+RESULTS: -[[file:test.png]] - -+ =C-c C-x C-v= ⇒ Show images inline -+ Mermaid supported headers: - - ~file~ to name the svg/png/pdf output - - ~width~ or ~height~ or the resulting image - - ~theme~ used, such as ~default, forest, dark, neutral~, for foreground entities - - ~background-color~ such as ~transparent, red, #F0F0F0~ - * The transparent option is nice ^_^ -+ You can insert new lines using ~
~ and horizontal rules via ~
~. - Similarly you can use other HTML tags such as ~
~; if you have too many - you can make CSS file then use the header argument ~:css-file~. -+ Add “non-breaking space” with ~ ~. This is a forced extra space and it prevents - a line break at its location. You can insert it repeatedly, but for two spaces - use ~ ~ and for four spaces use ~ ~. - -If link text cuts off prematurely, use extra space /with/ a newline: ~A-- text - 
-->B~. - -*Warning*: JavaScript has some issues when working with Unicode and so, being a -JavaScript utility, ~mermaid~ hangs when Unicode is used. On the upside, being a -JavaScript utility, ~mermaid~ entities can have [[https://mermaid-js.github.io/mermaid/#/flowchart?id=interaction][arbitrary code attached]] to them to -be executed upon clicks ---for use in browsers. -- However, the Greek letters are supported; e.g., γ and Σ. - -See [[https://mermaid-js.github.io/mermaid/#/flowchart?id=nodes-amp-shapes][here]] for possible node shapes. - -#+begin_quote -After forming an intricate diagram of related design patterns, I had to use a -number of HTML notions, such as =, , ,

,  ,
,
,
-
= and it was a bit more than I would have liked. In particular, the only -way to change font size was to use the deprecated HTML tag == or heading tags -like =

=; even worse, the resulting PDF image did not look nice ---I had to -stretch it out. - -The command line tool is *lacking functionality* and so the docs are not helpful. -E.g., I cannot produce pie charts using the command line tool. -#+end_quote - -***   [[https://revealjs.com/?transition=zoom#/][Reveal.JS]] -- The HTML Presentation Framework +**   ~M-n,p~: Word-at-Point Navigation ╱╲ Automatic highlighting current symbol/word :Disabled: :PROPERTIES: -:CUSTOM_ID: https-revealjs-com-transition-zoom-Reveal-JS-The-HTML-Presentation-Framework +:CUSTOM_ID: M-n-p-Word-at-Point-Navigation +:header-args: :tangle no :END: -Org-mode documents can be transformed into beautiful slide decks -with [[https://github.com/yjwen/org-reveal/blob/master/Readme.org][org-reveal]] with the following two simple lines. +Let's mimic the ~C-n,p~ constructs from line to word, so that unoccupied ~M-n,p~ now +serve to take us to the next or previous instance of the word under the +cursor. This is less intrusive than searching ~C-s~ or listing all occurrences ~M-s +o~. Moreover, let's automaticly highlight current symbols, words under the cursour, and cycle +quickly between highlkighted locations with +doc:ahs-forward / [[kbd:M-→]] and +doc:ahs-backward / [[kbd:M-←]]. #+BEGIN_SRC emacs-lisp -(use-package ox-reveal - :custom (org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")) +;; Default: M-→/← moves to the next/previous instance of the currently highlighted word +;; These are already meaningful commands in Org-mode, so we avoid these key re-bindings in Org-mode; TODO. +(use-package auto-highlight-symbol) +;; :hook ((text-mode . auto-highlight-symbol-mode) +;; (prog-mode . auto-highlight-symbol-mode))) +;; +;; This breaks Org Exports; e.g., +;; C-c C-e h o ⇒ Match data clobbered by buffer modification hooks #+END_SRC -# MA: ??? It looks like ox-reveal is being abandoned in favor of org-re-reveal, -# a fork compatible with org-mode 9.2? -For example, execute, ~C-x C-e~ after the closing parenthesis of, the -following block to see an example slide-deck (─‿‿─) -#+BEGIN_SRC emacs-lisp :tangle no -(progn (shell-command "curl https://raw.githubusercontent.com/yjwen/org-reveal/696613edef0fe17a9c53146f79933fe7c4101100/Readme.org >> Trying_out_reveal.org") - (switch-to-buffer (find-file "Trying_out_reveal.org")) - (org-reveal-export-to-html-and-browse)) +Let's also quickly replace the word at point: +#+BEGIN_SRC emacs-lisp +(defun my/symbol-replace (replacement) + "Replace all standalone symbols in the buffer matching the one at point." + (interactive (list (read-from-minibuffer "Replacement for thing at point: " nil))) + (save-excursion + (let ((symbol (or (thing-at-point 'symbol) (error "No symbol at point!")))) + (beginning-of-buffer) + ;; (query-replace-regexp symbol replacement) + (replace-regexp (format "\\b%s\\b" (regexp-quote symbol)) replacement)))) #+END_SRC -Org-mode exporting, ~C-c C-e~, now includes an option ~R~ for such reveal slide decks. -:Hide: -[[https://alhassy.github.io/next-700-module-systems/proposal/defence-slides.html#/sec-title-slide][Here]] ([[https://raw.githubusercontent.com/alhassy/next-700-module-systems/master/proposal/defence-slides.org][source]]) is an example of org-reveal slides where I add a number to each page, -use multiple columns, and extend the margins perhaps a bit too much. -:End: +(Unfortunately, as it currently is, there is no universal argument support: ~C-u +2 M-p~ does /not/ take you to the second previous instance of a word ---the prefix +is instead ignored. We can quickly form a work-around with doc:defhydra ---which +gives us temoporary modal modes.) -Two dimensional slides may be a bit new to some people, so I like to -give viewers an option, in tiny font, to view the slide-deck -continuously and remind them that ~?~ provides useful shortcuts. +Finally, let's use [[kbd:M-n ╱ M-p ╱ M-']] to move to the next, previous, and +replace the word at point /but also/ to give us a meanu to repeat any of these +actions. #+BEGIN_SRC emacs-lisp -(setq org-reveal-title-slide "

%t

%a

- - -⟪ Flattened View ; Press ? for Help ⟫ - -") -#+END_SRC -One should remove the ~&showNotes=true~ if they do not want to include -speaker notes in the flattened view. +(defmacro my/make-navigation-hydra (initial-action) + `(defhydra word-navigation + (:body-pre (,initial-action)) "Word-at-point Navigation" + ("n" ahs-forward "Next instance") + ("p" smartscan-symbol-go-backward "Previous instance") + ("r" my/symbol-replace "Replace all occurances") + ("s" ahs-display-stat "Stats"))) -Within the flatenned view, one may wish to ~CTRL/CMD+P~ then save the -resulting PDF locally. +;; (bind-key* str func) ≈ (global-set-key (kbd str) func) +(bind-key* "M-n" (my/make-navigation-hydra ahs-forward)) +(bind-key* "M-p" (my/make-navigation-hydra ahs-backward)) +(bind-key* "M-'" (my/make-navigation-hydra my/symbol-replace)) +#+END_SRC -*** Org-mode ⇐ HTML :Disabled: +An alternative is the [[https://github.com/mickeynp/smart-scan][smart-scan]] library ---which does the same thing, but does +not provide the highlighting occurances at point. +# The default symbol replacement is [[https://github.com/mickeynp/smart-scan/issues/23][over-zealous]] in that it replaces sub-terms +# occurring as parts of larger words. Let's do something about that. +** Letter-based Navigation :Disabled: :PROPERTIES: -:CUSTOM_ID: Org-mode-HTML +:CUSTOM_ID: Letter-based-Navigation +:header-args: :tangle no :END: +At a glance of possible positions, across windows, +and a key to jump there is a feature provided to us by [[https://github.com/winterTTr/ace-jump-mode/wiki/AceJump-FAQ][ace-jump]] +---here is an [[https://www.youtube.com/watch?feature=player_embedded&v=UZkpmegySnc#!][emacs-rocks 2-minute video]]. -The following let's us copy htlm into org format using eww, Emacs' built-in web browser. -#+BEGIN_SRC emacs-lisp :tangle no -;; See: https://emacs.stackexchange.com/questions/7171/paste-html-into-org-mode -(use-package org-eww - :quelpa (org-eww :fetcher git :url "https://github.com/Fuco1/org-mode.git")) -#+END_SRC - -It does not work as I'd like, but may prove useful to have around. - -+ Possibly useful: Open a webpage with ~M-x eww~ then toggle ~M-x read-only-mode~ to - edit the text, say for notes or deletions, as you read! No need to copy-paste. - --------------------------------------------------------------------------------- +For example, =C-c SPC m= greys our all windows and places a red +letter at the start of any word that begins with /m/, then I may +press a letter to jump to the associated position in the +associated window. Using ~C-u C-c SPC~ and ~C-u C-u C-c SPC~ let +me jump to any character or to any visible line. -[[https://github.com/alphapapa/org-web-tools][org-web-tools]] claims to /view, capture, and archive Web pages in Org-mode/; this -may be a very useful tool. +➩ Super simple use case: Fix your eyes on an occurence of a word, then ~C-c SPC~ + to quickly jump to it so as to edit the sentence in which it occurs. + - It's like ~C-s~ but more lightweight. #+begin_src emacs-lisp -(use-package org-web-tools - :config - ;; Insert an Org-mode link to the URL in the clipboard or kill-ring. Downloads - ;; the page to get the HTML title. - ;; (bind-key* "C-c C-l" #'org-web-tools-insert-link-for-url) ;; Instead, see my/org-insert-link-dwim below. - ) -#+end_src -Other useful functions, needing pandoc: -doc:org-web-tools-insert-web-page-as-entry and -doc:org-web-tools-convert-links-to-page-entries. +(use-package ace-jump-mode -#+begin_src emacs-lisp -;; C-u C-c C-l ⇒ Paste URL with title, WITHOUT prompting me for anything. -;; C-c C-l ⇒ Prompt me for title. -(bind-key* "C-c C-l" - (lambda () (interactive) - (call-interactively - (if current-prefix-arg - #'org-web-tools-insert-link-for-url - #'my/org-insert-link-dwim)))) -;; From: -(defun my/org-insert-link-dwim () - "Like `org-insert-link' but with personal dwim preferences. + :config (bind-key* "C-c SPC" 'ace-jump-mode)) -- When text is selected, use that as the link description --and prompt for link type -- When a URL is in the clipboard, use that as the link type -- On an existing Org link, prompt to alter the link then to alter the description -- With a ‘C-u’ prefix, prompts for a file to link to. - - It is relative to the current directory; use ‘C-u C-u’ to get an absolute path. +;; See ace-jump issues to configure for use of home row keys. +#+end_src -It fallsback to `org-insert-link' when possible. +There is a newer and somewhat more powerful package, [[https://github.com/abo-abo/avy][avy]], which accompishes the +same goal. It uses a tree style to jumipng: Locations are given two letter +combinations, one presses one letter to jump to a group of text, then another +letter to jump somewhere in that grouping. I prefer ace-jump since it greys +everthing out, whereas avy surrounds jump locations with a box. +Here is an [[https://www.youtube.com/watch?v=zar4GsOBU0g][emacs-doom 6-minute video]] for avy. -Functin Source: https://xenodium.com/emacs-dwim-do-what-i-mean/" - (interactive) - (let* ((point-in-link (org-in-regexp org-link-any-re 1)) - (clipboard-url (when (string-match-p "^http" (current-kill 0)) - (current-kill 0))) - (region-content (when (region-active-p) - (buffer-substring-no-properties (region-beginning) - (region-end))))) - (cond ((and region-content clipboard-url (not point-in-link)) - (delete-region (region-beginning) (region-end)) - (insert (org-make-link-string clipboard-url region-content))) - ((and clipboard-url (not point-in-link)) - (insert (org-make-link-string - clipboard-url - (read-string "title: " - (with-current-buffer (url-retrieve-synchronously clipboard-url) - (dom-text (car - (dom-by-tag (libxml-parse-html-region - (point-min) - (point-max)) - 'title)))))))) - (t - (call-interactively 'org-insert-link))))) -#+end_src +There is also [[https://github.com/tam17aki/ace-isearch][ace-isearch]] for bridinging different navgiational methods ---one +begins incremental search, ~s-f~, then according to a pause and length of input, +one of the navgiational methods, such as isearch or avy or helm-swoop, will be +begun. I'm okay with using ~C-s~ for helm-swoop and ~C-c SPC~ for ace-jump, and +still have ~s-f~ for incremental search, which I hardly use. -* COMMENT Programming -:PROPERTIES: -:CUSTOM_ID: Programming -:END: +*What is bind-keys**? -Herein we configure utilites for version control, function and variable lookup, -and template expansion for inescapably repetitive scenarios. +Major modes provide specfic use and so their bindings always take precedence +over global bindings ---e.g., the major mode binding may do what the global does +but with extra mode-specfic behaviour, such as indentation. Other times, a major +mode's binding simply uses the same key presses with completely unrelated +behaviour. If we want to avoid having our global keybindings shadowed by a +major mode, we may use the ~bind-key*~ /macro/ of ~use-package~, or the ~bind-keys*~ +/macro/ when there are multiple keys; these are macros, not clauses. ---These +essentially creates a dedicated minor mode behind the scenes, which saves us the +work of [[https://emacs.stackexchange.com/a/358/10352][doing it ourselves]]. -TODO: Fix these docs -# TODO: + Checkout branches/PRs with doc : my/gh-checkout -# + See all company related PRs with doc : w-PRs -# + Quickly look up language/library docs /within/ Emacs with kbd:C-c_d. +| | ~(bind-keys* (k₁ . f₁) … (kₙ . fₙ))~ | +| ≈ | These keybindings override all minor modes that use keys =kᵢ=. | +Of course we can also use it without the asterisk; e.g.: #+begin_src emacs-lisp -(when my/work-machine? - (setq doom-modeline-buffer-file-name-style 'truncate-except-project)) +;; C-x o ⇒ Switch to the other window +;; C-x O ⇒ Switch back to the previous window +(bind-key "C-x O" (lambda () (interactive) (other-window -1))) #+end_src -** COMMENT Quickly Run Code Snippets +**   =C-c e n,p=: Taking a tour of one's edits :Disabled: :PROPERTIES: -:CUSTOM_ID: Quickly-Run-Code-Snippets +:CUSTOM_ID: C-c-e-n-p-Taking-a-tour-of-one's-edits +:header-args: :tangle no :END: +This package allows us to move around the edit points of a buffer /without/ +actually undoing anything. We even obtain a brief description of what happend at +each edit point. This seems useful for when I get interrupted or lose my train +of thought: Just press [[kbd:C-c e p]] to see what I did recently and where ---the +“e” is for “e”dit. -Sometimes we want to quickly run some code /without making a dedicated file/ or -with a file but /without remembering the terminal incantation to do so/, enter -~quickrun~. Anywhere, we can select a snippet of code and run TODO. doc.quickrun-region -to execute that snippet after selecting the associated programming language, or -TODO. doc.quickrun-replace-region if we want the results in-line. If our language of -choice does not exist, we can [[https://github.com/emacsorphanage/quickrun#user-defined-command][easily add support for it]]. - -#+begin_src emacs-lisp -;; In any programming buffer, “M-x quickrun” to execute that program. -;; Super useful when wanting to quickly test things out, in a playground. -;; -;; E.g., Make a new file named “hello.py” containing “print "hi"”, then “M-x quickrun”. -;; -;; Enable “quickrun-autorun-mode” to run code after every save. -(use-package quickrun - ;; ⇒ “C-c C-r” to see output, “q” to close output - ;; ⇒ “C-u C-c C-r” prompts for a language (Useful when testing snippets different from current programming mode) - ;; ⇒ In a non-programming buffer, “C-c C-r” runs selected region. - :defer nil - :config (bind-key* "C-c C-r" - (lambda (&optional start end) - (interactive "r") - (if (use-region-p) - (quickrun-region start end) - (quickrun current-prefix-arg))))) -#+end_src +#+BEGIN_SRC emacs-lisp +;; Give me a description of the change made at a particular stop. +(use-package goto-chg -Example... -#+begin_src emacs-lisp -(system-packages-ensure "rust") ;; Rust Compiler -;; Select the following then press C-c C-r: fn main() { println!("Hello, World!"); } -#+end_src + :custom (glc-default-span 0)) -Actually, let's get a [[https://github.com/brotzeit/rustic#intro][full Rust development environment for Emacs]] (which also -has [[https://github.com/brotzeit/rustic#org-babel][great support for Org-babel]].) +(my/defhydra "C-c e" "Look at them edits!" bus + :\ ("p" goto-last-change "Goto nᵗʰ last change") + ("n" goto-last-change-reverse "Goto more recent change")) +#+END_SRC + +Compare this with ~C-x u~, or ~undo-tree-visualise~, wherein undos are actually performed. + +Notice, as a hydra, I can use ~C-c e~ followed by any combination of ~p~ and ~n~ to +navigate my recent edits /without/ having to supply the prefix each time. + +** visual-regexp +:PROPERTIES: +:CUSTOM_ID: visual-regexp +:END: #+begin_src emacs-lisp -(use-package rustic :defer nil) -;; Open any Rust file, and run “M-x lsp” which will then prompt you to install -;; rust-analyzer, the rust LSP. -;; -;; LSP for Rust ⇒ Goto definition (M-. / ⌘-l), code completion with types and -;; docstrings, colourful documentation on hover, “Run [Test] | Debug” overlays, -;; super nice stuff! Run “M-!”/[M-x company-show-doc-buffer] if you want the doc in a colourful buffer. +;; While constructing the regexp in the minibuffer, get live visual feedback for the (group) matches. +;; E.g., try: M-% use-\(.+?\) \(.+\)\b ENTER woah \1 and \2 ;; -;; Below, hover over “Vec” and see nice, scrollable, colourful docs on vectors. -;; let v:Vec<_> = vec![1, 2, 3]; +;; C-u M-% do to regexp replace, without querying. +(use-package visual-regexp -;; The offical Rust toolchain installer -(system-packages-ensure "rustup") -(shell-command "rustup update") + :config (define-key global-map (kbd "M-%") + (lambda (&optional prefix) (interactive "P") (call-interactively (if prefix #'vr/replace #'vr/query-replace))))) #+end_src -:More_on_rust: - -Now, C-c C-c C-d on something to get help/description of it. -(setq rustic-racer-rust-src-path "/Users/musa/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/") +** LaTeX ⇐ Org-Mode :Disabled: +:PROPERTIES: +:CUSTOM_ID: Org-Mode-PDF-HTML +:header-args: :tangle no +:END: -# Get Rustup, load it into current terminal session -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -source ~/.bash_profile -source ~/.bashrc -type rustup +In this section we consider the Org-mode export for PDFs (LaTeX). +For example, we account for LaTeX citations. -# Get stuff -rustup update -rustup component add rust-src -cargo +nightly install racer -rustup toolchain install nightly -rustup component add rustc-dev --toolchain=nightly -cargo +nightly install racer +# Installing LaTex +# $ brew install --cask mactex +# $ eval "$(/usr/libexec/path_helper)" +# $ type pdflatex # => pdflatex is /Library/TeX/texbin/pdflatex +# +# The ~minted~ environment can't appear as an argument to another command; a +# [[https://tex.stackexchange.com/questions/102416/error-when-compiling-a-minted-listings-inside-a-memoir-subfloat][work-around]] is to use a ‘box’. Learn more about LaTeX boxes [[https://latexref.xyz/Boxes.html][here]]. -:End: -** COMMENT C-x C-e ∷ REPL-driven development for ELisp / NodeJS / Python / Etc ! +*** Get LaTeX: :PROPERTIES: -:CUSTOM_ID: C-x-C-e-REPL-driven-development-for-NodeJS +:CUSTOM_ID: Get-LaTeX :END: -| /Evaluate code and see the results inline ---A feedback loop that is faster than ever!/ | - -Within Emacs, kbd:C-x_C-e evaluates a Lisp expression /anywhere/; e.g., -at the end of ~(message-box "hello world")~ press ~C-x C-e~ to see a greeting. -# Likewise, evaluating a variable shows you its value: user-full-name. - -- We ran some code /without/ explicitly running an interpreter/repl/compiler! +# +begin_src shell +# time brew cask install mactex-no-gui +# +end_src -- This is known as “REPL driven development” (RDD): - There is a running REPL server for your language, implicitly in the - background, and your editor (say with ~C-x C-e~) will send it a line (or a - selected region) of code for evaluation; we then see the result as an - overlay in our current buffer. +#+begin_src emacs-lisp +(system-packages-ensure "mactex") +#+end_src - + You /choose/ which code gets (re)evaluated. + - This is a redistribution of TeX Live specifically for macOS. + - We get the 4GB version since it has [[https://tex.stackexchange.com/a/1041/69371][everything]] and so + do not need to worry about missing style files. + - This took about 12 minutes on my machine. -- A quick introduction to RDD can be viewed at [[https://purelyfunctional.tv/lesson/what-is-repl-driven-development/][PurelyFunctional.tv]]. + Restart Emacs, enter =$e^{i \cdot \pi} + 1 = 0$= then press kbd:C-c_C-x_C-l to + have it rendered inline. -*** ELisp -:PROPERTIES: -:CUSTOM_ID: ELisp -:END: + + *Minted:* Get tool for colourful code snippets for LaTeX ---see “minted” in + the main article. -By default, Emacs Lisp's kbd:C-x_C-e shows results only in the minibuffer; near -the bottom of the screen. Let's also have evaluation results displayed as inline -overlays ---at the location that the user, us, is actually looking/working; -rather than forcing their eyes to shift up&down when writing&evaluating. + #+begin_src emacs-lisp +(system-packages-ensure "pygments") +#+end_src -+ kbd:C-u_C-x_C-e inserts the evaluation result at point; kbd:C-u_0_C-x_C-e does - so /without truncating/ lengthy output. -+ Read this [[https://karthinks.com/software/an-elisp-editing-tip/][Sweet & short blog/GIFs]] on practical uses of kbd:C-x_C-e when working with Lisp. + + *Not anymore:* Get a neato PDF presentation console: =brew install pdfpc= + - With =pdfpc myfile.pdf= you get a nice timer and multiple views + of the current slide and upcoming slides ---with support for multiple monitors. + - Install ScreenBrush, from the Apple Store, for easily drawing/annotating my + screen ---e.g., when I'm giving a virtual lecture to my students. + - An alternative is =brew install mupdf= then =mupdf-gl myfile.pdf= and press + =f= for fullscreen then =a= for adding/adorning drawings ---it was too + rough to use live. -#+begin_src emacs-lisp -;; Evaluation Result OverlayS for Emacs Lisp -(use-package eros - :defer nil - :init (eros-mode t)) -#+end_src + Finally, within Emacs: =M-x pdf-tools-install= -# In particular, this gives us the Lisp function -# `eros--eval-overlay', which evaluates its argument X -# and produces an overlay of the result at position P. -# E.g., (eros--eval-overlay "hola" (point-min)) -# Shows a momentary overlay at the first line. +:Possibly_breaking_toc-org: +#+BEGIN_SRC emacs-lisp :tangle no +;; Use 3 headlines of export, which is the default +;; (setq org-export-headline-levels 4) -*** JavaScript +;; no numbers by default at export +;; (setq org-export-with-section-numbers nil) +#+END_SRC +:End: +*** Working with Citations :Disabled:Not_Used: :PROPERTIES: -:CUSTOM_ID: JavaScript +:CUSTOM_ID: Working-with-Citations :END: -Let's setup RDD for JavaScript ---by having a NodeJS repl server running in the background. - -- See [[https://github.com/anonimitoraf/skerrick][skerrick: REPL-driven development for Javascript]] for animated GIFs; it - works with modules as well. +# TODO Fix me, breaking Github Actions test setup -#+begin_src emacs-lisp -(use-package skerrick - :defer nil - :init - ;; Needs to be run on the very first install of skerrick. Or when you want to upgrade. - (unless (equal (shell-command-to-string "type skerrick") "skerrick not found\n") - (skerrick-install-or-upgrade-server-binary))) + [[https://github.com/jkitchin/org-ref][An exquisite system]] for handling references. -;; Should be run in a JS buffer; it is buffer specific. -;; (skerrick-start-server) + The following entity will display useful data + when the mouse hovers over it (•̀ᴗ•́)و If you click on it, then you're + in for a lot of super neat stuff, such as searching for the pdf online! -;; Now main function, entry point is: -;; M-x skerrick-eval-region -#+end_src +cite:agda_overview ( In HTML export, the citation doesn't link anywhere. ) -Let's provide a quick keyboard shortcut. E.g., kbd:C-x_C-e evaluates ELisp, so let's mimic that for JS buffers: -#+begin_src emacs-lisp -(require 'js) ;; Defines js-mode-map + #+BEGIN_SRC emacs-lisp :tangle no +(unless noninteractive -;; Evaluate a region, if any is selected; otherwise evaluate the current line. -(bind-key - "C-x C-e" (lambda () - (interactive) - (if (use-region-p) - (skerrick-eval-region) - (beginning-of-line) - (set-mark-command nil) - (end-of-line) - (skerrick-eval-region) - (pop-mark))) - 'js-mode-map) -#+end_src +(use-package org-ref + :custom ;; Files to look at when no “╲bibliography{⋯}” is not present in a file. + ;; Most useful for non-LaTeX files. + (reftex-default-bibliography '("~/thesis-proposal/papers/References.bib")) + (bibtex-completion-bibliography (car reftex-default-bibliography)) + (org-ref-default-bibliography reftex-default-bibliography)) +;; Quick BibTeX references, sometimes. +(use-package helm-bibtex) +(use-package biblio) ) +#+END_SRC -Trying something else instead of skerrick, until -https://github.com/anonimitoraf/skerrick/issues/7 is resolved. + Execute ~M-x helm-bibtex~ or =C-c ]= and, say, enter =emacs= and you will be + presented with all the entries in the bib database that mention ‘emacs’. Super + cool stuff. Moreover, if no such entries exist, then we can look some up + using the interface! -In any JS file, I just press ~C-x C-e~ and the JS interpreter is brought up with the evaluation results shown: -#+begin_src emacs-lisp -(use-package js-comint :defer nil) -(define-key js-mode-map (kbd "C-x C-e") (lambda () (interactive) (call-interactively #'js-comint-repl) (other-window -1) (js-comint-send-last-sexp))) -#+end_src + Read the manual [[https://github.com/jkitchin/org-ref/blob/master/org-ref.org][online]] or better yet as an org-file with ~M-x org-ref-help~. -For instance, -#+begin_src js :tangle no -// Start the server... then -// On each line and press C-x C-e -let a = "hello" -let b = "world" -(a + ' ' + b).toUpperCase() -// The final line should show: HELLO WORLD -#+end_src + This is an Org-mode application since the citations have tooltips and export + nicely to LaTeX & HTML via the Org-mode exporter. -**** Preserving the context +*** Bibliography & Coloured LaTeX using Minted :PROPERTIES: -:CUSTOM_ID: Preserving-the-context +:CUSTOM_ID: Bibliography-Coloured-LaTeX-using-Minted :END: -When testing an application, you might notice a bug in a particular context ----i.e., a particular configuration in the app. - -1. The classic approach is to kill the app; i.e., stop the server that is, well, - serving the app. -2. Solve the problem. -3. Try to get back to the configuration, context, you were in beforehand and - check that the problem has been resolved. +Execute the following for bibliography references as well as minted Org-mode +uses the Minted package for source code highlighting in PDF/LaTeX ---which in +turn requires the pygmentize system tool. +#+BEGIN_SRC emacs-lisp +(setq org-latex-listings 'minted + org-latex-packages-alist '(("" "minted")) + org-latex-pdf-process + '("pdflatex -shell-escape -output-directory %o %f" + "biber %b" + "pdflatex -shell-escape -output-directory %o %f" + "pdflatex -shell-escape -output-directory %o %f")) +#+END_SRC -A better approach is to ignore the bookkeeping steps, 1&3, and just do step 2. -For that, there are numerous packages: -- [[https://github.com/mishoo/livenode/][livenode: Live-code your NodeJS applications]] ⨾⨾ [[https://vimeo.com/60636079][Video demo]] ~11min ⨾⨾ Last - updated 2013 -- [[https://github.com/skeeto/skewer-mode][skewer-mode: Live web development in Emacs]] ⨾⨾ [[https://www.youtube.com/watch?v=4tyTgyzUJqM&ab_channel=Skeeto][Silent video demo]] ~5min ⨾⨾ Last - updated 2020 -- [[https://github.com/swank-js/swank-js][swank-js: Swank backend for Node.JS and in-browser JavaScript]] ⨾⨾ Last updated - 2015 +For faster pdf generation, possibly with errors, consider invoking: +#+begin_example emacs-lisp +(setq org-latex-pdf-process + '("pdflatex -interaction nonstopmode -output-directory %o %f")) +#+end_example -For more RDD alternatives in Emacs, see https://github.com/anonimitoraf/skerrick/issues/8. +By default, Org exports LaTeX using the ~nonstopmode~ option, which tries +its best to produce a PDF ---which ignores typesetting errors altogether, +which is not necessary ideal when using LaTeX. -*** Python, etc +*** COMMENT Excellent PDF Viewer :PROPERTIES: -:CUSTOM_ID: Python-etc +:CUSTOM_ID: Excellent-PDF-Viewer :END: -It /seems/ to be /super easy/ to add such a support for other languages, such as Python. -See the final comment [[https://karthinks.com/software/an-elisp-editing-tip/][here]] for the tiny change required. -** COMMENT devdocs +Need to (re)build the epdfinfo program, do it now ? (y or n) n + +-------------------------------------------------------------------------------- + +Let's install the [[https://github.com/politza/pdf-tools][pdf-tools]] library for viewing PDFs in Emacs. +#+BEGIN_SRC emacs-lisp +(use-package pdf-tools + ; :init (system-packages-ensure "pdf-tools") + :custom (pdf-tools-handle-upgrades nil) + (pdf-info-epdfinfo-program "/usr/local/bin/epdfinfo") + :config (pdf-tools-install)) + +;; Now PDFs opened in Emacs are in pdfview-mode. +#+END_SRC + +Besides the expected PDF viewing utilities, such as search, annotation, and continuous scrolling; +with a simple mouse right-click, we can even select a ‘midnight’ rendering mode which may be +easier on the eyes. For more, see the brief [[https://www.dailymotion.com/video/x2bc1is][pdf-tools-tourdeforce]] demo. +** HTML ⇐ Org-mode :PROPERTIES: -:CUSTOM_ID: devdocs +:CUSTOM_ID: HTML-Org-mode :END: -#+begin_src emacs-lisp -;; 1. Get docs of a languages: M-x devdocs-install -;; 2. Lookup docs: [C-u] M-x devdocs-lookup -;; 𝟚. Lookup docs: [C-u] C-c d -(use-package devdocs - :defer nil - :bind ("C-c d" . #'devdocs-lookup) - :config - (when nil ;; “C-x C-e” the following once. - (cl-loop for lang in '(javascript ramda typescript html css sass - vue~3 vuex~4 vue_router~4 "angularjs~1.6" - nginx webpack~5 web_extensions - ;; - eslint jest jq jsdoc prettier - mocha chai jasmine - ;; - bash docker~19 git homebrew elisp - ;; - postgresql~14 redis sqlite - ;; - rust ruby~3 minitest "rails~7.0") - do (devdocs-install (list (cons 'slug (format "%s" lang))))))) -#+end_src -** COMMENT How do I do something? +In this section we consider the Org-mode exporters for PDFs and +HTMLs. For example, we account for LaTeX citations and reliable HTML +anchors. + +#+BEGIN_SRC emacs-lisp +(use-package htmlize ) +;; Main use: Org produced htmls are coloured. +;; Can be used to export a file into a coloured html. +#+END_SRC + +*** Ensuring Useful HTML Anchors :PROPERTIES: -:CUSTOM_ID: How-do-I-do-something +:CUSTOM_ID: Ensuring-Useful-HTML-Anchors :END: - When programming, sometimes you just gotta Google “how do I do ⋯”. - - The usual process is (1) open a browser, (2) make a Google query, (3) look - at StackOverflow's most upvoted answer for your query, (4) copy-paste the - code solution/example to your editor; [(5) get distracted by interesting - things you'd like to read]. - - Better would be to use the [[https://github.com/gleitz/howdoi][howdoi]] tool, which gives instant coding answers - for common questions via the command line. - - Below, my /Emacs Lisp/ function doc : howdoi let's me reduce the 4-step process - to just 2 steps: Write your query /anywhere/ then call ~M-x howdoi~ on it to - replace the query with the answer. (Or ~C-u M-x howdoi~ to see the full answer - and a link to it on StackOverflow.) +Upon HTML export, each tree heading is assigned an ID to be used for hyperlinks. +Default IDs are something like ~org1957a9d~, which does not endure the test of time: +Re-export will produce a different id. Here's a rough snippet to generate +IDs from headings, by replacing spaces with hyphens, for headings without IDs. - | /⚡ Never open your browser to look for help again ⚡/ | +#+BEGIN_SRC emacs-lisp +(defun my/ensure-headline-ids (&rest _) + "Org trees without a -#+begin_src emacs-lisp -(system-packages-ensure "howdoi") +All non-alphanumeric characters are cleverly replaced with ‘-’. -(cl-defun howdoi (&optional show-full-answer) - "Instantly insert coding answers. +If multiple trees end-up with the same id property, issue a +message and undo any property insertion thus far. -Replace a query with a code solution; replace it with an entire -answer if a prefix is provided. +E.g., ↯ We'll go on a ∀∃⇅ adventure + ↦ We'll-go-on-a-adventure +" + (interactive) + (let ((ids)) + (org-map-entries + (lambda () + (org-with-point-at (point) + (let ((id (org-entry-get nil "CUSTOM_ID"))) + (unless id + (thread-last (nth 4 (org-heading-components)) + (s-replace-regexp "[^[:alnum:]']" "-") + (s-replace-regexp "-+" "-") + (s-chop-prefix "-") + (s-chop-suffix "-") + (setq id)) + (if (not (member id ids)) + (push id ids) + (message-box "Oh no, a repeated id!\n\n\t%s" id) + (undo) + (setq quit-flag t)) + (org-entry-put nil "CUSTOM_ID" id)))))))) -Example usage: +;; Whenever html & md export happens, ensure we have headline ids. +(advice-add 'org-html-export-to-html :before 'my/ensure-headline-ids) +(advice-add 'org-md-export-to-markdown :before 'my/ensure-headline-ids) +#+END_SRC - On a new line, write a question such as: +One may then use ~[[#my-custom-id]]~ to link to the entry with ~CUSTOM_ID~ +property ~my-custom-id~. - search and replace buffer Emacs Lisp +Interestingly, ~org-set-property~, ~C-c C-x p~, lets us insert a property +from a selection of available ones, then we'll be prompted for a value +for it from a list of values you've used elsewhere. This is useful for +remaining consistent for when trees share similar properties. - Then invoke ‘M-x howdoi’ anywhere on the line - to get a code snippet; or ‘C-u M-x howdoi’ to get a full answer to your query. -" - (interactive "P") - (let ((query (s-collapse-whitespace (substring-no-properties (thing-at-point 'line)))) - (flag (if show-full-answer "-a" ""))) - (beginning-of-line) - (kill-line) - (insert (shell-command-to-string (format "howdoi %s %s" query flag))))) -#+end_src -** Sleek Semantic Selection +*** Clickable Headlines :PROPERTIES: -:CUSTOM_ID: Sleek-Semantic-Selection +:CUSTOM_ID: Clickable-Headlines :END: -Super sleek way to select regions: Anywhere press kbd:⌘-r to select the current -word, press it again to select sentence, then again for the current paragraph, -then more to get the current section. +By default, HTML export generates ID's to headlines so they may be referenced +to, but there is no convenient way to get at them to refer to a particular +heading. The following spell fixes this issue: Headlines are now clickable, +resulting in a link to the headline itself. #+begin_src emacs-lisp -(use-package expand-region - :defer nil - :bind (("s-r" . #'er/expand-region))) +;; Src: https://writepermission.com/org-blogging-clickable-headlines.html +(setq org-html-format-headline-function + (lambda (todo todo-type priority text tags info) + "Format a headline with a link to itself." + (let* ((headline (get-text-property 0 :parent text)) + (id (or (org-element-property :CUSTOM_ID headline) + (ignore-errors (org-export-get-reference headline info)) + (org-element-property :ID headline))) + (link (if id + (format "%s" id text) + text))) + (org-html-format-headline-default-function todo todo-type priority link tags info)))) #+end_src -You can watch an introductory ~3 minute video to expand-region at [[http://emacsrocks.com/e09.html][Emacs Rocks!]]. - -That is, /repeated ⌘+r expands the selection to the next logical segment of text:/ -In writing this means “Word, sentence, paragraph”, and in programming this means -“identifier, then incrementally larger scopes”. +*Warning:* The header cannot already be a link! Otherwise you get cyrptic and +unhelpful error =(wrong-type-argument plistp :section-number)=; which then +pollutes the current Emacs session resulting in stange =nil= errors after =C-x C-s=, +thereby forcing a full Emacs restart. Instead, you need at least one portion of +each heading to be not a link. -** Managing Processes/Servers from within Emacs ---Work-specific functions +1. Need to have a custom id declared. + #+BEGIN_SRC org :tangle no + :PROPERTIES: + :CUSTOM_ID: my-header + :END: +#+END_SRC +2. Failing headers: =* [[link]]= nor =* ~code~= nor =* $math$=. + - Any non-link text /before/ it will work: =ok [[link]]=. + * Using Unicode non-breaking space ‘ ’ is ok. + - Text /only after/ the link is insufficient. +*** HTML “Folded Drawers” :PROPERTIES: -:CUSTOM_ID: Managing-Processes-Servers-from-within-Emacs-Work-specific-functions +:CUSTOM_ID: HTML-Folded-Drawers :END: -Let's make a few interactive Emacs Lisp functions to reduce the amount of time I -need to be in a terminal. I'll use the /prefix ~“w-”~ for work stuff/. Example -tasks: -+ Start/stop my servers -+ Interactively select an app to be opened in the browser -+ Do database migrations/rollbacks +#+BEGIN_SRC emacs-lisp +(defun my/org-drawer-format (name contents) + "Export to HTML the drawers named with prefix ‘fold_’, ignoring case. -⟨ Obfuscated with lorem ipsum text. ⟩ -[[file:images/services-dashboard.png]] +The resulting drawer is a ‘code-details’ and so appears folded; +the user clicks it to see the information therein. +Henceforth, these are called ‘fold drawers’. --------------------------------------------------------------------------------- +Drawers without such a prefix may be nonetheless exported if their +body contains ‘:export: t’ ---this switch does not appear in the output. +Thus, we are biased to generally not exporting non-fold drawers. -Not using this, a bit too verbose to setup for each service but, more -accurately, does not Just Workᵀᴹ for my needs. -#+begin_src emacs-lisp -;; “M-x prodigy”, then press “s” to start a service; “S” to stop it; “$” to see it; “r”estart -(use-package prodigy :disabled t) - ;; C-h v prodigy-services ⇒ See possible properties. -#+end_src +One may suspend export of fold drawers by having ‘:export: nil’ +in their body definition. -*** my/defaliases -:PROPERTIES: -:CUSTOM_ID: my-defaliases -:END: -#+begin_src emacs-lisp -(defalias 'defaliases 'my/defaliases) -(defmacro my/defaliases (src &rest tgts) - "Provide names TGTS as synonymous aliases for SRC, for discovarability. +Fold drawers naturally come with a title. +Either it is specfied in the drawer body by ‘:title: ⋯’, +or otherwise the drawer's name is used with all underscores replaced +by spaces. +" + (let* ((contents′ (replace-regexp-in-string ":export:.*\n?" "" contents)) + (fold? (s-prefix? "fold_" name 'ignore-case)) + (export? (string-match ":export:\s+t" contents)) + (not-export? (string-match ":export:\s+nil" contents)) + (title′ (and (string-match ":title:\\(.*\\)\n" contents) + (match-string 1 contents)))) -Often a function SRC can be construed from different perspectives, names, purposes TGTS. -Another example is when I define things with the ‘my/’ prefix, but also want to use them without. + ;; Ensure we have a title. + (unless title′ (setq title′ (s-join " " (cdr (s-split "_" name))))) -Example use: (my/defaliases view-hello-file greet-others learn-about-the-world) + ;; Output + (cond + ((and export? (not fold?)) contents′) + (not-export? nil) + (fold? + (thread-last contents′ + (replace-regexp-in-string ":title:.*\n" "") + (format "
+ %s + %s
" title′)))))) -In particular: (my/defaliases OLD NEW) ≈ (defalias 'NEW 'OLD)." - `(--map (eval (quote (defalias `,it (quote ,src)))) (quote ,tgts))) -#+end_src +(setq org-html-format-drawer-function 'my/org-drawer-format) -*** Making unkillable buffers & shells -:PROPERTIES: -:CUSTOM_ID: Making-unkillable-buffers-shells -:END: -#+begin_details "Making unkillable buffers & shells" -#+begin_src emacs-lisp -(defun my/declare-unkillable-buffer (name) - (add-hook 'kill-buffer-query-functions - `(lambda () (or (not (equal (buffer-name) ,name)) - (progn (message "Not allowed to kill %s, burying instead; otherwise use “M-x force-kill”" (buffer-name)) - (bury-buffer)))))) +#+END_SRC -(my/defaliases my/force-kill force-kill w-force-kill) -(cl-defun my/force-kill (&optional buffer-name) - (interactive) - (-let [kill-buffer-query-functions nil] - (if buffer-name - (kill-buffer buffer-name) - (kill-current-buffer)) - (ignore-errors (delete-window)))) +With the following invocations we only see the odd indexed ‘hello’s, where the +latter two are folded up. +#+BEGIN_SRC org :tangle no +:this-drawer-is-exported: +:export: t +hello 1 +:End: -(cl-defun my/run-unkillable-shell (command &optional (buffer-name command)) - "Example use: (my/run-unkillable-shell \"cd ~/my-noejds-project; npm run dev\" \"my-nodejs-project\")" - (-let [it (get-buffer buffer-name)] - (if it - (switch-to-buffer-other-window it) - (async-shell-command command buffer-name) - (my/declare-unkillable-buffer buffer-name)))) -#+end_src -#+end_details +:this-drawer-is-NOT-exported: +hello 2 +:End: -*** my/work-links +:fold_This_drawer_has_a_title_in_the_body: +:title: I am the drawer title 0 + +hello 3 +:End: + +:fold_This_drawer_is_NOT_exported: +:title: Why are we here? +:export: nil + +hello 4 +:End: + +:fold_I_am_the_drawer_title_1: + +hello 5 +:End: +#+END_SRC + +I doubt I could show an example in the Github README, since no HTML export is +happening using my setup. In case you're reading this on my blog, which has +exported HTML. Here's the example: +:fold_hello_world: +:title: ¡Hola! Buenas tardes, Amigo +Hey bud, hope you're enjoying this read ^_^ +:End: + +Now that I've written this, I'm thinking it may have been preferably to use an org-block…? + +*** Diagrams with Mermaid ---Not Reccommended :Disabled: :PROPERTIES: -:CUSTOM_ID: my-work-links +:CUSTOM_ID: Diagrams-with-Mermaid-Not-Reccommended +:header-args: :tangle no :END: -In my private ~work.el~ file, I have declarations of the form ~(my/work-links -"REPO" "https://⟨COMPANY⟩.atlassian.net/browse/REPO-%s")~ so that I can write -things like ~REPO:1234~ to get a nice green bold link in Org-mode that will take -me to that Jira link. I also have similar links to take me to the Github -repositories, backlogs, and Kanban boards. +Let's try out an alternative to PlantUML ---covered below in §[[Workflow States]]. -#+begin_src emacs-lisp - (cl-defmacro my/work-links (type url &optional (export-display '(format "%s-%s" type label))) - "Given a link of TYPE with a URL, produce the correct org-link. +First, let's get the tool. +#+BEGIN_SRC shell :tangle no +npm install mermaid.cli +sudo git clone git@github.com:arnm/mermaid-layer.git ~/.emacs.d/private/mermaid +#+END_SRC - EXPORT-DISPLAY is string-valued term that may mention the symbolic names ‘type’ and ‘label’. - This is how the link looks upon export." - `(org-link-set-parameters - ,type - :follow (lambda (label) (browse-url (format ,url label))) - :export (lambda (label description backend) - (-let [full-url (format ,url label)] - (pcase backend - ('html (format "%s" full-url (-let [type ,type] ,export-display))) - ('latex (format "\\href{%s}{FM-%s}" full-url label)) - (_ full-url)))) - :face '(:foreground "green" :weight bold - :underline "blue" :overline "blue"))) - #+end_src +Then, let's get the associated ~mermaid~ package. +#+BEGIN_SRC emacs-lisp +(use-package ob-mermaid + :custom ob-mermaid-cli-path "~/node_modules/.bin/mmdc") +#+END_SRC -*** w-start/stop-services +Then, =C-c C-c= on the following: +#+BEGIN_SRC mermaid :file test.png :theme neutral :background-color green :tangle no :results replace :eval never +sequenceDiagram +A-->B: Works! +#+END_SRC + +#+RESULTS: +[[file:test.png]] + ++ =C-c C-x C-v= ⇒ Show images inline ++ Mermaid supported headers: + - ~file~ to name the svg/png/pdf output + - ~width~ or ~height~ or the resulting image + - ~theme~ used, such as ~default, forest, dark, neutral~, for foreground entities + - ~background-color~ such as ~transparent, red, #F0F0F0~ + * The transparent option is nice ^_^ ++ You can insert new lines using ~
~ and horizontal rules via ~
~. + Similarly you can use other HTML tags such as ~
~; if you have too many + you can make CSS file then use the header argument ~:css-file~. ++ Add “non-breaking space” with ~ ~. This is a forced extra space and it prevents + a line break at its location. You can insert it repeatedly, but for two spaces + use ~ ~ and for four spaces use ~ ~. + +If link text cuts off prematurely, use extra space /with/ a newline: ~A-- text + 
-->B~. + +*Warning*: JavaScript has some issues when working with Unicode and so, being a +JavaScript utility, ~mermaid~ hangs when Unicode is used. On the upside, being a +JavaScript utility, ~mermaid~ entities can have [[https://mermaid-js.github.io/mermaid/#/flowchart?id=interaction][arbitrary code attached]] to them to +be executed upon clicks ---for use in browsers. +- However, the Greek letters are supported; e.g., γ and Σ. + +See [[https://mermaid-js.github.io/mermaid/#/flowchart?id=nodes-amp-shapes][here]] for possible node shapes. + +#+begin_quote +After forming an intricate diagram of related design patterns, I had to use a +number of HTML notions, such as =, , ,

,  ,
,
,
+
= and it was a bit more than I would have liked. In particular, the only +way to change font size was to use the deprecated HTML tag == or heading tags +like =

=; even worse, the resulting PDF image did not look nice ---I had to +stretch it out. + +The command line tool is *lacking functionality* and so the docs are not helpful. +E.g., I cannot produce pie charts using the command line tool. +#+end_quote + +***   [[https://revealjs.com/?transition=zoom#/][Reveal.JS]] -- The HTML Presentation Framework :PROPERTIES: -:CUSTOM_ID: w-start-stop-services +:CUSTOM_ID: https-revealjs-com-transition-zoom-Reveal-JS-The-HTML-Presentation-Framework :END: -#+begin_src emacs-lisp -(defvar my/services nil "List of all services defined; used with `w-start-services' and `w-stop-services'.") -(defun w-start-services () - (interactive) - (cl-loop for 𝑺 in my/services - do (funcall (intern (format "w-start-%s" 𝑺))))) +Org-mode documents can be transformed into beautiful slide decks +with [[https://github.com/yjwen/org-reveal/blob/master/Readme.org][org-reveal]] with the following two simple lines. -(defun w-stop-services () - (interactive) - (cl-loop for 𝑺 in my/services - do (funcall (intern (format "w-stop-%s" 𝑺))))) -#+end_src +#+BEGIN_SRC emacs-lisp +(use-package ox-reveal -*** w-status-of-services + :custom (org-reveal-root "https://cdn.jsdelivr.net/npm/reveal.js")) +#+END_SRC +# MA: ??? It looks like ox-reveal is being abandoned in favor of org-re-reveal, +# a fork compatible with org-mode 9.2? + +For example, execute, ~C-x C-e~ after the closing parenthesis of, the +following block to see an example slide-deck (─‿‿─) +#+BEGIN_SRC emacs-lisp :tangle no +(progn (shell-command "curl https://raw.githubusercontent.com/yjwen/org-reveal/696613edef0fe17a9c53146f79933fe7c4101100/Readme.org >> Trying_out_reveal.org") + (switch-to-buffer (find-file "Trying_out_reveal.org")) + (org-reveal-export-to-html-and-browse)) +#+END_SRC + +Org-mode exporting, ~C-c C-e~, now includes an option ~R~ for such reveal slide decks. + +:Hide: +[[https://alhassy.github.io/next-700-module-systems/proposal/defence-slides.html#/sec-title-slide][Here]] ([[https://raw.githubusercontent.com/alhassy/next-700-module-systems/master/proposal/defence-slides.org][source]]) is an example of org-reveal slides where I add a number to each page, +use multiple columns, and extend the margins perhaps a bit too much. +:End: + +Two dimensional slides may be a bit new to some people, so I like to +give viewers an option, in tiny font, to view the slide-deck +continuously and remind them that ~?~ provides useful shortcuts. +#+BEGIN_SRC emacs-lisp +(setq org-reveal-title-slide "

%t

%a

+ + +⟪ Flattened View ; Press ? for Help ⟫ + +") +#+END_SRC +One should remove the ~&showNotes=true~ if they do not want to include +speaker notes in the flattened view. + +Within the flatenned view, one may wish to ~CTRL/CMD+P~ then save the +resulting PDF locally. + +** C-c C-l Org-mode ⇐ HTML :PROPERTIES: -:CUSTOM_ID: w-status-of-services +:CUSTOM_ID: Org-mode-HTML :END: -#+begin_src emacs-lisp -;; It takes about ~3 seconds to build the Status of Services page, so let's jump to it if it's already built, and the user/me can request a refresh, if need be. -(global-set-key (kbd "M-S-SPC") - (lambda () (interactive) - (-let [buf (get-buffer "Status of Services")] (if buf (switch-to-buffer buf) (w-status-of-services))))) -;; -;; Since M-S-SPC brings up the transient menu, and most commands close the status buffer or are transient, we get the perception that the transient menu is "sticky"; i.e., stuck to the buffer, even though this is not true. I do not yet know how to make a transient menu stuck to a buffer. -;; -#+end_src - -#+begin_src emacs-lisp -(defun w-status-of-services () +The following let's us copy htlm into org format using eww, Emacs' built-in web browser. +#+BEGIN_SRC emacs-lisp :tangle no +;; See: https://emacs.stackexchange.com/questions/7171/paste-html-into-org-mode +(use-package org-eww + + :quelpa (org-eww :fetcher git :url "https://github.com/Fuco1/org-mode.git")) +#+END_SRC + +It does not work as I'd like, but may prove useful to have around. + ++ Possibly useful: Open a webpage with ~M-x eww~ then toggle ~M-x read-only-mode~ to + edit the text, say for notes or deletions, as you read! No need to copy-paste. + +-------------------------------------------------------------------------------- + +[[https://github.com/alphapapa/org-web-tools][org-web-tools]] claims to /view, capture, and archive Web pages in Org-mode/; this +may be a very useful tool. + +#+begin_src emacs-lisp +(use-package org-web-tools + + :config + ;; Insert an Org-mode link to the URL in the clipboard or kill-ring. Downloads + ;; the page to get the HTML title. + ;; (bind-key* "C-c C-l" #'org-web-tools-insert-link-for-url) ;; Instead, see my/org-insert-link-dwim below. + ) +#+end_src +Other useful functions, needing pandoc: +doc:org-web-tools-insert-web-page-as-entry and +doc:org-web-tools-convert-links-to-page-entries. + +#+begin_src emacs-lisp +;; C-u C-c C-l ⇒ Paste URL with title, WITHOUT prompting me for anything. +;; C-c C-l ⇒ Prompt me for title. +(bind-key* "C-c C-l" + (lambda () (interactive) + (call-interactively + (if current-prefix-arg + #'org-web-tools-insert-link-for-url + #'my/org-insert-link-dwim)))) +;; From: +(defun my/org-insert-link-dwim () + "Like `org-insert-link' but with personal dwim preferences. + +- When text is selected, use that as the link description --and prompt for link type +- When a URL is in the clipboard, use that as the link type +- On an existing Org link, prompt to alter the link then to alter the description +- With a ‘C-u’ prefix, prompts for a file to link to. + - It is relative to the current directory; use ‘C-u C-u’ to get an absolute path. + +It fallsback to `org-insert-link' when possible. + +Functin Source: https://xenodium.com/emacs-dwim-do-what-i-mean/" + (interactive) + (let* ((point-in-link (org-in-regexp org-link-any-re 1)) + (clipboard-url (when (string-match-p "^http" (current-kill 0)) + (current-kill 0))) + (region-content (when (region-active-p) + (buffer-substring-no-properties (region-beginning) + (region-end))))) + (cond ((and region-content clipboard-url (not point-in-link)) + (delete-region (region-beginning) (region-end)) + (insert (org-make-link-string clipboard-url region-content))) + ((and clipboard-url (not point-in-link)) + (insert (org-make-link-string + clipboard-url + (read-string "title: " + (with-current-buffer (url-retrieve-synchronously clipboard-url) + (dom-text (car + (dom-by-tag (libxml-parse-html-region + (point-min) + (point-max)) + 'title)))))))) + (t + (call-interactively 'org-insert-link))))) +#+end_src + +* TODO FIXME: repl/java ignores newlines but that means single-line-comments should be stripped out /first/! +* TODO loading work.el +#+begin_src emacs-lisp +(load-file "~/Desktop/work.el") +#+end_src + +* TODO RDD methods: repl/jshell/insert-last-output ;; repl/jshell/copy-last-output-to-clipboard ;; repl/jshell/show-last-output-in-own-buffer [An Org mode buffer with the last-input and last output, headings] +* TODO RDD + +TODO: Make repl load all company class files, that way I have them upon start up. +OR: Provide a command `//!load ~/dir` which recursively loads/imports all java files? <---Probably this! + +#+begin_src emacs-lisp :tangle init.el +(setq enable-local-variables :safe) + +(use-package repl-driven-development) +(setq repl-driven-development/echo-duration 10) ;; seconds + +;; Sometimes I see a bunch of shell incantations in a README or something and I'd like to execute them right there and then, +;; and not have to bother with copying them over to a terminal and execute there. As such, here's a quick key binding to execute +;; shell commands from anywhere. +(repl-driven-development [C-x C-t] "bash" :prompt "bash-3.2\\$") +(when nil ;; example +NAME=musa +echo hello $NAME +echo hello $CLASSPATH +) + + +;; Set “C­x C­j” to evaluate Java code in a background REPL. +(repl-driven-development [C-x C-j] + ;; enable assertions, and add everything installed, via mvn, in scope; and imports all Java SE packages by default! + (format "jshell --class-path %s --enable-preview -R -ea" + (shell-command-to-string "find ~/.m2/repository -name \"*.jar\" -type f | tr '\n' ':'")) + :prompt "jshell>") +;; Do not use the “--startup JAVASE” option. +;; +;; List.of(1) | Error: +;; | reference to List is ambiguous +;; | both interface java.util.List in java.util and class java.awt.List in java.awt match +;; | List.of(1) +;; | ^--^ +;; + +(defun mvn (groupId artifactId) + "Quickly install a library from Maven Central." + (async-shell-command (format "mvn org.apache.maven.plugins:maven-dependency-plugin:2.8:get -Dartifact=%s:%s:LATEST:jar:sources" groupId artifactId))) +(when nil ;; Example use of C-x C-j + + 1 + 22 + + TestNG is a testing framework inspired from JUnit and NUnit but introducing some new functionalities that make it more powerful and easier to use. It supports test configured by annotations, data-driven testing, parametric tests, etc. +(mvn "org.testng" "testng") + +import org.testng.*; + /imports // Now can see org.testng at the end of the list + +java -jar /Users/musa/.m2/repository/org/antlr/antlr4/4.13.0/antlr4-4.13.0-complete.jar + +(mvn "org.antlr" "antlr4") ;; C-x C-e +;; Now re-start the Java C-x C-j repl via a C-x C-e (lame! not ergonomic!) +;; Now check that you have access to antrl in your repl by importing it and looking at one of its classes: +;; ⦃ jshell --class-path /Users/musa/.m2/repository/org/antlr/antlr4-runtime/4.13.0/antlr4-runtime-4.13.0.jar ⦄ +import org.antlr.v4.runtime.*; +CommonTokenStream.class +;; MA: This is the runtime, I want the actual tool! +;; +;; +;; MA: to be ergonomic, make “C-u -1 C-x C-j” restart the repl, by *default* this means kill the repl process and restart it. +;; +;; MA: TODO: For working with Lombok annotations, use the jshell `/reset --class-path` command to include the lombok compiled file into +;; the current Jshell session. +;; +;; See: https://stackoverflow.com/questions/74084364/how-to-use-lombok-in-jshell +;; For example, +;; +;; $ jshell +;; > var x = 5 +;; > import org.antlr.v4.runtime.*; // CRASHES since antlr is not in scope +;; > /reset --class-path /Users/musa/.m2/repository/org/antlr/antlr4-runtime/4.13.0/antlr4-runtime-4.13.0.jar +;; > import org.antlr.v4.runtime.*; // WORKS, yay +;; > x // CRASHES, not in scope +;; +;; (mvn "org.projectlombok" "lombok") +;; import lombok.*; +;; @lombok.Data class Test { private String name; } +;; new Test().equals(new Test()) + +import org.apache.commons.lang3.StringUtils; +StringUtils.class; +/imports // JShell command to list all imports, it now contains apache! + + +// Guava is the Google core libraries for Java +import com.google.common.collect.ImmutableMap; + +ImmutableMap.of(1, "A", 2, "B") // ⇒ {1=A, 2=B} + +// (mvn "com.google.code.gson" "gson") +// Then C-x C-e to update the repl definition of C-x C-j to include the updated gson library. +import com.google.gson.*; +String json = new Gson().toJson(Map.of("me", List.of(1, 2,3), "you", Map.of("Love", "Lisp", "Hate", "Verbosity"))) + +;; End examples +) +#+end_src +** COMMENT Quickly Run Code Snippets +:PROPERTIES: +:CUSTOM_ID: Quickly-Run-Code-Snippets +:END: + +Sometimes we want to quickly run some code /without making a dedicated file/ or +with a file but /without remembering the terminal incantation to do so/, enter +~quickrun~. Anywhere, we can select a snippet of code and run TODO. doc.quickrun-region +to execute that snippet after selecting the associated programming language, or +TODO. doc.quickrun-replace-region if we want the results in-line. If our language of +choice does not exist, we can [[https://github.com/emacsorphanage/quickrun#user-defined-command][easily add support for it]]. + +#+begin_src emacs-lisp +;; In any programming buffer, “M-x quickrun” to execute that program. +;; Super useful when wanting to quickly test things out, in a playground. +;; +;; E.g., Make a new file named “hello.py” containing “print "hi"”, then “M-x quickrun”. +;; +;; Enable “quickrun-autorun-mode” to run code after every save. +(use-package quickrun + ;; ⇒ “C-c C-r” to see output, “q” to close output + ;; ⇒ “C-u C-c C-r” prompts for a language (Useful when testing snippets different from current programming mode) + ;; ⇒ In a non-programming buffer, “C-c C-r” runs selected region. + :defer nil + :config (bind-key* "C-c C-r" + (lambda (&optional start end) + (interactive "r") + (if (use-region-p) + (quickrun-region start end) + (quickrun current-prefix-arg))))) +#+end_src + +Example... +#+begin_src emacs-lisp +(system-packages-ensure "rust") ;; Rust Compiler +;; Select the following then press C-c C-r: fn main() { println!("Hello, World!"); } +#+end_src + +Actually, let's get a [[https://github.com/brotzeit/rustic#intro][full Rust development environment for Emacs]] (which also +has [[https://github.com/brotzeit/rustic#org-babel][great support for Org-babel]].) +#+begin_src emacs-lisp +(use-package rustic :defer nil) +;; Open any Rust file, and run “M-x lsp” which will then prompt you to install +;; rust-analyzer, the rust LSP. +;; +;; LSP for Rust ⇒ Goto definition (M-. / ⌘-l), code completion with types and +;; docstrings, colourful documentation on hover, “Run [Test] | Debug” overlays, +;; super nice stuff! Run “M-!”/[M-x company-show-doc-buffer] if you want the doc in a colourful buffer. +;; +;; Below, hover over “Vec” and see nice, scrollable, colourful docs on vectors. +;; let v:Vec<_> = vec![1, 2, 3]; + +;; The offical Rust toolchain installer +(system-packages-ensure "rustup") +(shell-command "rustup update") +#+end_src + +:More_on_rust: + +Now, C-c C-c C-d on something to get help/description of it. +(setq rustic-racer-rust-src-path "/Users/musa/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/") + +# Get Rustup, load it into current terminal session +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +source ~/.bash_profile +source ~/.bashrc +type rustup + +# Get stuff +rustup update +rustup component add rust-src +cargo +nightly install racer +rustup toolchain install nightly +rustup component add rustc-dev --toolchain=nightly +cargo +nightly install racer + +:End: + +** COMMENT C-x C-e ∷ REPL-driven development for ELisp / NodeJS / Python / Etc ! +:PROPERTIES: +:CUSTOM_ID: C-x-C-e-REPL-driven-development-for-NodeJS +:END: + +| /Evaluate code and see the results inline ---A feedback loop that is faster than ever!/ | + +Within Emacs, kbd:C-x_C-e evaluates a Lisp expression /anywhere/; e.g., +at the end of ~(message-box "hello world")~ press ~C-x C-e~ to see a greeting. +# Likewise, evaluating a variable shows you its value: user-full-name. + +- We ran some code /without/ explicitly running an interpreter/repl/compiler! + +- This is known as “REPL driven development” (RDD): + There is a running REPL server for your language, implicitly in the + background, and your editor (say with ~C-x C-e~) will send it a line (or a + selected region) of code for evaluation; we then see the result as an + overlay in our current buffer. + + + You /choose/ which code gets (re)evaluated. + +- A quick introduction to RDD can be viewed at [[https://purelyfunctional.tv/lesson/what-is-repl-driven-development/][PurelyFunctional.tv]]. + +*** ELisp +:PROPERTIES: +:CUSTOM_ID: ELisp +:END: + +By default, Emacs Lisp's kbd:C-x_C-e shows results only in the minibuffer; near +the bottom of the screen. Let's also have evaluation results displayed as inline +overlays ---at the location that the user, us, is actually looking/working; +rather than forcing their eyes to shift up&down when writing&evaluating. + ++ kbd:C-u_C-x_C-e inserts the evaluation result at point; kbd:C-u_0_C-x_C-e does + so /without truncating/ lengthy output. ++ Read this [[https://karthinks.com/software/an-elisp-editing-tip/][Sweet & short blog/GIFs]] on practical uses of kbd:C-x_C-e when working with Lisp. + +#+begin_src emacs-lisp +;; Evaluation Result OverlayS for Emacs Lisp +(use-package eros + :defer nil + :init (eros-mode t)) +#+end_src + +# In particular, this gives us the Lisp function +# `eros--eval-overlay', which evaluates its argument X +# and produces an overlay of the result at position P. +# E.g., (eros--eval-overlay "hola" (point-min)) +# Shows a momentary overlay at the first line. + +*** JavaScript +:PROPERTIES: +:CUSTOM_ID: JavaScript +:END: + +Let's setup RDD for JavaScript ---by having a NodeJS repl server running in the background. + +- See [[https://github.com/anonimitoraf/skerrick][skerrick: REPL-driven development for Javascript]] for animated GIFs; it + works with modules as well. + +#+begin_src emacs-lisp +(use-package skerrick + :defer nil + :init + ;; Needs to be run on the very first install of skerrick. Or when you want to upgrade. + (unless (equal (shell-command-to-string "type skerrick") "skerrick not found\n") + (skerrick-install-or-upgrade-server-binary))) + +;; Should be run in a JS buffer; it is buffer specific. +;; (skerrick-start-server) + +;; Now main function, entry point is: +;; M-x skerrick-eval-region +#+end_src + +Let's provide a quick keyboard shortcut. E.g., kbd:C-x_C-e evaluates ELisp, so let's mimic that for JS buffers: +#+begin_src emacs-lisp +(require 'js) ;; Defines js-mode-map + +;; Evaluate a region, if any is selected; otherwise evaluate the current line. +(bind-key + "C-x C-e" (lambda () + (interactive) + (if (use-region-p) + (skerrick-eval-region) + (beginning-of-line) + (set-mark-command nil) + (end-of-line) + (skerrick-eval-region) + (pop-mark))) + 'js-mode-map) +#+end_src + + +Trying something else instead of skerrick, until +https://github.com/anonimitoraf/skerrick/issues/7 is resolved. + +In any JS file, I just press ~C-x C-e~ and the JS interpreter is brought up with the evaluation results shown: +#+begin_src emacs-lisp +(use-package js-comint :defer nil) +(define-key js-mode-map (kbd "C-x C-e") (lambda () (interactive) (call-interactively #'js-comint-repl) (other-window -1) (js-comint-send-last-sexp))) +#+end_src + +For instance, +#+begin_src js :tangle no +// Start the server... then +// On each line and press C-x C-e +let a = "hello" +let b = "world" +(a + ' ' + b).toUpperCase() +// The final line should show: HELLO WORLD +#+end_src + +**** Preserving the context +:PROPERTIES: +:CUSTOM_ID: Preserving-the-context +:END: + +When testing an application, you might notice a bug in a particular context +---i.e., a particular configuration in the app. + +1. The classic approach is to kill the app; i.e., stop the server that is, well, + serving the app. +2. Solve the problem. +3. Try to get back to the configuration, context, you were in beforehand and + check that the problem has been resolved. + +A better approach is to ignore the bookkeeping steps, 1&3, and just do step 2. +For that, there are numerous packages: +- [[https://github.com/mishoo/livenode/][livenode: Live-code your NodeJS applications]] ⨾⨾ [[https://vimeo.com/60636079][Video demo]] ~11min ⨾⨾ Last + updated 2013 +- [[https://github.com/skeeto/skewer-mode][skewer-mode: Live web development in Emacs]] ⨾⨾ [[https://www.youtube.com/watch?v=4tyTgyzUJqM&ab_channel=Skeeto][Silent video demo]] ~5min ⨾⨾ Last + updated 2020 +- [[https://github.com/swank-js/swank-js][swank-js: Swank backend for Node.JS and in-browser JavaScript]] ⨾⨾ Last updated + 2015 + +For more RDD alternatives in Emacs, see https://github.com/anonimitoraf/skerrick/issues/8. + +*** Python, etc +:PROPERTIES: +:CUSTOM_ID: Python-etc +:END: + +It /seems/ to be /super easy/ to add such a support for other languages, such as Python. +See the final comment [[https://karthinks.com/software/an-elisp-editing-tip/][here]] for the tiny change required. + +** Setting a Classpath +One way you can connect external code to be accessed by the jshell tool is setting the classpath. The classpath can be set on the command-line: + + % jshell --class-path myOwnClassPath +Your classpath should point to directories or JAR files with the packages you wish to access. The code must be compiled into class files. Code in the default package (also known as the unnamed package) cannot be accessed from JShell. Once you have set the classpath these packages can be imported: + +jshell> import my.cool.code.* +You also use the '/env' command to set the classpath: + +jshell> /env --class-path myOwnClassPath +| Setting new options and restoring state. +Note that this command resets the execution state, reloading any current snippets with the new classpath setting (or other environment setting). + +Source: The excellent [[https://cr.openjdk.org/~rfield/tutorial/JShellTutorial.html][JShell Tutorial]] by Robert Field. + +** The built-in feedback modes cannot have their prompt|truncation|format changed, but we can inheirt from a built-in mode, then change those properties. +*** format: Notify me when imports succeed, and show types of results +In the format, inherited from normal mode, an import doesn't give any feedback, and the type of a value is not shown. The format of snippet output is set with the /set format command. Try this, without settings, for some mode, to see the current format settings: + +my mode: /set format mine +#+begin_src java :tangle no +import java.beans.* // Submit this twice and both times see nothing :-( + +/set mode myNewMode normal -command +/set feedback myNewMode +/help /set format // There is extensive help on this command with /help /set format. + +/set format myNewMode display "{pre}added import {name}{post}" import-added +/set format myNewMode display "{pre}re-added import {name}{post}" import-modified,replaced + +import java.awt.Graphics // Submit this twice and see two distinct messages 🕹 +#+end_src + +Let's also change the defult “var ==> value” output to include the type of the resulting value. +#+begin_src java :tangle no +// The payload does not indicate the type of this thing, I'd like to know what kind of data I'm working with! +Locale.CANADA.getUnicodeLocaleAttributes() // $5 ==> [] + +/set format myNewMode result "{type} {name} = {value}{post}" added,modified,replaced-primary-ok + +2 + 4 // int $40 = 6 +String me = "hello" // Emits: String me ⇒ "hello" +Locale.CANADA.getUnicodeLocaleAttributes() // Set $41 = [] + + +// Aside: JAVASE Imports all Java SE packages, this is big and will cause a noticeable startup delay +#+end_src + +*** truncation +/set mode myNewMode normal -command +/set feedback myNewMode +/set truncation myNewMode 40000 +var big = IntStream.range(0, 40000).mapToObj(x -> x).toList() +big + +;; This should be an flet around eros call in repl-driven-development macro. +;; (defun window-width (&rest args) most-positive-fixnum) +;; https://github.com/xiongtx/eros/blob/master/eros.el#L202 + +* Adding support for “//!use” & “//!omit” top level repl commands +#+begin_src emacs-lisp +;; Adding support for “//!use” & “//!omit” top level repl commands +(setq repl/jshell/classpath (shell-command-to-string "find ~/.m2/repository -name \"*.jar\" -type f | tr '\n' ':'")) +(advice-add 'repl/jshell + :around (lambda (repl &rest args) + (if (equal nil current-prefix-arg) + ;; No prefix supplied + (progn + (setq rdd---current-input (s-replace-regexp "\n" "" (s-trim-left + (if (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end)) + (substring-no-properties (thing-at-point 'line)))))) + (if (s-starts-with? "//!use" rdd---current-input) + (-let [jar (s-trim (s-chop-prefix "//!use" rdd---current-input))] + (repl/top-level//!use jar)) + (if (s-starts-with? "//!omit" rdd---current-input) + (-let [jar (s-trim (s-chop-prefix "//!omit" rdd---current-input))] + (repl/top-level//!omit jar)) + ;; otherwise business as usual + (apply repl args)))) + (pcase current-prefix-arg + (-1 + ;; reset classpath to default, then business as usual. + (setq repl/jshell/classpath (shell-command-to-string "find ~/.m2/repository -name \"*.jar\" -type f | tr '\n' ':'")) + (apply repl args)) + (999 + (message-box "It worked")) + ;; otherwise business as usual + (t (apply repl args)))))) + +;; remove all advice +;; (-let [sym 'repl/jshell] (advice-mapc (lambda (advice _props) (advice-remove sym advice)) sym)) + +(cl-defun repl/top-level//!omit (str) + (with-temp-buffer + (setq repl/jshell/classpath (s-replace-regexp (format ":[^:]*%s[^:]*:" str) ":" repl/jshell/classpath)) + (insert "/env --class-path ") + (insert repl/jshell/classpath) + (repl/jshell (point-min) (point-max)))) + +(cl-defun repl/top-level//!use (str) + "If the given jar cannot be added successful, the existing classpath remains untouched. + +Return to your default classpath by invoking the repl with the -1 prefix. + +Example usage: + + //!use ~/path/to/compiled/java/classes + import com.x.y.z; + +Where directory hierarchy com/x/y/z denotes a Java package under the above //!use path. +" + (with-temp-buffer + (setq repl/jshell/classpath (concat (s-trim str) ":" repl/jshell/classpath)) + (insert "/env --class-path ") + (insert repl/jshell/classpath) + (repl/jshell (point-min) (point-max)))) +#+end_src + +* TODO jRead -- ;; Nesting does not work. +#+begin_src emacs-lisp +(add-hook 'repl-driven-development/output-hook + (lambda (output) + (when (equal current-prefix-arg '(4)) ;; insertion prefix + (insert "\n") + (insert (jRead (s-replace-regexp "^.* ==> " "" repl-driven-development-current--output))) + ;; Disable default insertion behaviour. + (setq current-prefix-arg nil)) + output)) +;; +;; +;; TODO: Add to java init/prologue +;; JShell has a truncation for outputs exceeding 1k chars, let's increase the threshold to size 40k chars. +;; /set truncation 40000 +;; +;; TODO: Add to docs of repl-driven-development-current--output, that if you see “...” then chances are that you have +;; exceeded the truncation threshold for your repl. Consider increasing the threshold, if possible, and add it to the :init/:prologue of your repl. +;; +;; +;; record Person(String name, int age, double zindex) { } +;; var people = List.of(new Person("Jasim", 72, .5), new Person("Kathy", 82, 2.78), new Person("Al-hassy, Musa", 31, 1 + 2)) +;; people // Press C-u C-x C-j to get the executable Java represenation inserted +(cl-defun jRead (a-pretty-printed-record--diff) + "Reads executable Java code from pretty-printed record representations. + +If called interactively, copies executable Java to clipboard; otherwise +returns the executable Java as a string." + (interactive "sJava Read: ") + (let* ((sep (format " ⟨%s⟩ " (gensym))) + (rx-sep (regexp-quote sep)) + (result + (--> + a-pretty-printed-record--diff + ;; "[Person[name=Jasim, age=72, zindex=.5], Person[name=Kathy, age=82, zindex=2.78], Person[name=Musa, age=31, zindex=3]]" + ;; "Person[name=Al-hassy, Musa, age=thirty and one, years=31]" + ;; Identify field delimiters from commas that may be present in the payloads. + ;; Drop all field names from pretty-printed records: `RecordName[fieldName=value goes to `RecordName[value]` + (s-replace-regexp ", [^ \\[]*=" (concat sep sep) it) ;; ⇒⇒⇒ "Person[name=Al-hassy, Musa ⟨g760⟩ ⟨g760⟩ thirty and one ⟨g760⟩ ⟨g760⟩ 31]" + ;; Replace `RecordName[value]` with `new RecordName(value]` + (s-replace-regexp "\\([A-Za-z]+\\)\\[[^ ]*=" (concat "new \\1(" sep) it :lower-case) ;; ⇒⇒⇒ "new Person( ⟨g761⟩ Al-hassy, Musa ⟨g761⟩ ⟨g761⟩ thirty and one ⟨g761⟩ ⟨g761⟩ 31]" + ;; All remaining `]` can now be replaced by `)`. + (s-replace-regexp "\\]" (concat sep ")") it) ;; ⇒⇒⇒ "new Person( ⟨g779⟩ Al-hassy, Musa ⟨g779⟩ ⟨g779⟩ thirty and one ⟨g779⟩ ⟨g779⟩ 31 ⟨g779⟩ )" + ;; As a precautionary measure, let's enclose all arguments in quotes + (s-replace-regexp (format "%s\\(.*?\\)%s" rx-sep rx-sep) ", \"\\1\"" it) ;; ⇒⇒⇒ "new Person(, \"Al-hassy, Musa\", \"thirty and one\", \"31\")" + ;; Remove quotes around numbers + (s-replace-regexp "\"\\([0-9.]+.?[0-9.]*\\)\"" "\\1" it) ;; ⇒⇒⇒ "new Person(,\"Al-hassy, Musa\",\"thirty and one\",31)" + ;; Remove malformed “(,” + (s-replace-regexp "(, " "(" it) + (s-replace-regexp (concat sep ")") ")" it) + ;; All remaining `[` are now `List.of` pretty-printings + (s-replace-regexp "\\[" "List.of(" it :lower-case)))) + (if (called-interactively-p) + (progn (kill-new result) + (message "Copied: %s" result)) + result))) + +(ert-deftest jRead-works-as-intended () + (should (equal (jRead "1 + 2") "1 + 2")) + (should (equal (jRead "Person[name=Musa]") "new Person(\"Musa\")")) + ;;(should (equal (jRead "Person[name=Musa, child=Person[name=Yusuf]]") 'TODO) ;; Nesting does not work. + (should (equal (jRead "Person[name=Al-hassy, Musa, age=thirty and one, years=31]") "new Person(\"Al-hassy, Musa\", \"thirty and one\", 31)")) + (should (equal (jRead "[Person[name=Jasim, age=72, zindex=.5], Person[name=Kathy, age=82, zindex=2.78], Person[name=Musa, age=31, zindex=3]]") + "List.of(new Person(\"Jasim\", 72, .5), new Person(\"Kathy\", 82, 2.78), new Person(\"Musa\", 31, 3))"))) +#+end_src +#+begin_src emacs-lisp +;; Other DSL rewrites I want NOW + +(cl-defun jRead/rewrite (java-code) + (interactive "s") + (--> java-code + (loop for (rx replacement) in jRead-defs + do (setq it (s-replace-regexp rx replacement it)) + finally return it) + ;; (my/format-code #'java-mode it) ;; TODO: Not actually running; async issue probably. + kill-new + (yank))) + +;; Other DSL rewrites I want NOW +(defvar jRead-defs nil + "List of regexp-to-replacement pairs to apply to Java code.") + +;; (add-to-list 'jRead-defs '("new X(List.of(new Y(\\(.*?\\))))" "xy(\\1)")) + +(defun my/format-code (language-mode text) + "Format TEXT according to the given LANGUAGE-MODE. + +Example: + + ;; The following results in the standard 5-line convention for an if-then-else clause in Java + (my/format-code #'java-mode \"if(true) { System.out.println(2); } else { System.out.println(3); } \") + +The tool needed to do the formatting is specified in +`format-all-default-formatters', so ensure it's installed! +" + (with-temp-buffer + ;; (java-mode) + (funcall language-mode) + ;; Don't prompt me which formatter to use; just use the defaults. + ;; For example, for Java, I need to: brew install clang-format + (setq format-all-formatters format-all-default-formatters) + ;; (insert "cmd(new TclCat(List.of(new TclAtom(\"cmd4\"))), new TclQuote(List.of(new TclCat(List.of(new TclAtom(\"arg5\"))))), new TclCat(List.of(new TclAtom(\"$arg6\"))), new TclBrace(List.of(new TclCat(List.of(new TclAtom(\"a\"))), new TclCat(List.of(new TclAtom(\"r\"))), new TclCat(List.of(new TclAtom(\"g\"))), new TclCat(List.of(new TclAtom(\"7\"))))))") + (insert text) + (format-all-buffer) + (buffer-substring-no-properties (point-min) (point-max)))) +#+end_src + +* TODO Programming +:PROPERTIES: +:CUSTOM_ID: Programming +:END: + +Herein we configure utilites for version control, function and variable lookup, +and template expansion for inescapably repetitive scenarios. + +TODO: Fix these docs +# TODO: + Checkout branches/PRs with doc : my/gh-checkout +# + See all company related PRs with doc : w-PRs +# + Quickly look up language/library docs /within/ Emacs with kbd:C-c_d. + +#+begin_src emacs-lisp +(when my/work-machine? + (setq doom-modeline-buffer-file-name-style 'truncate-except-project)) +#+end_src + +** COMMENT devdocs +:PROPERTIES: +:CUSTOM_ID: devdocs +:END: +#+begin_src emacs-lisp +;; 1. Get docs of a languages: M-x devdocs-install +;; 2. Lookup docs: [C-u] M-x devdocs-lookup +;; 𝟚. Lookup docs: [C-u] C-c d +(use-package devdocs + :defer nil + :bind ("C-c d" . #'devdocs-lookup) + :config + (when nil ;; “C-x C-e” the following once. + (cl-loop for lang in '(javascript ramda typescript html css sass + vue~3 vuex~4 vue_router~4 "angularjs~1.6" + nginx webpack~5 web_extensions + ;; + eslint jest jq jsdoc prettier + mocha chai jasmine + ;; + bash docker~19 git homebrew elisp + ;; + postgresql~14 redis sqlite + ;; + rust ruby~3 minitest "rails~7.0") + do (devdocs-install (list (cons 'slug (format "%s" lang))))))) +#+end_src + +** COMMENT How do I do something? +:PROPERTIES: +:CUSTOM_ID: How-do-I-do-something +:END: + + When programming, sometimes you just gotta Google “how do I do ⋯”. + - The usual process is (1) open a browser, (2) make a Google query, (3) look + at StackOverflow's most upvoted answer for your query, (4) copy-paste the + code solution/example to your editor; [(5) get distracted by interesting + things you'd like to read]. + - Better would be to use the [[https://github.com/gleitz/howdoi][howdoi]] tool, which gives instant coding answers + for common questions via the command line. + - Below, my /Emacs Lisp/ function doc : howdoi let's me reduce the 4-step process + to just 2 steps: Write your query /anywhere/ then call ~M-x howdoi~ on it to + replace the query with the answer. (Or ~C-u M-x howdoi~ to see the full answer + and a link to it on StackOverflow.) + + | /⚡ Never open your browser to look for help again ⚡/ | + +#+begin_src emacs-lisp +(system-packages-ensure "howdoi") + +(cl-defun howdoi (&optional show-full-answer) + "Instantly insert coding answers. + +Replace a query with a code solution; replace it with an entire +answer if a prefix is provided. + +Example usage: + + On a new line, write a question such as: + + search and replace buffer Emacs Lisp + + Then invoke ‘M-x howdoi’ anywhere on the line + to get a code snippet; or ‘C-u M-x howdoi’ to get a full answer to your query. +" + (interactive "P") + (let ((query (s-collapse-whitespace (substring-no-properties (thing-at-point 'line)))) + (flag (if show-full-answer "-a" ""))) + (beginning-of-line) + (kill-line) + (insert (shell-command-to-string (format "howdoi %s %s" query flag))))) +#+end_src +** Sleek Semantic Selection +:PROPERTIES: +:CUSTOM_ID: Sleek-Semantic-Selection +:END: + +Super sleek way to select regions: Anywhere press kbd:⌘-r to select the current +word, press it again to select sentence, then again for the current paragraph, +then more to get the current section. + +#+begin_src emacs-lisp +(use-package expand-region + + :bind (("s-r" . #'er/expand-region))) +#+end_src + +You can watch an introductory ~3 minute video to expand-region at [[http://emacsrocks.com/e09.html][Emacs Rocks!]]. + +That is, /repeated ⌘+r expands the selection to the next logical segment of text:/ +In writing this means “Word, sentence, paragraph”, and in programming this means +“identifier, then incrementally larger scopes”. + +** COMMENT Managing Processes/Servers from within Emacs ---Work-specific functions +:PROPERTIES: +:CUSTOM_ID: Managing-Processes-Servers-from-within-Emacs-Work-specific-functions +:END: + +Let's make a few interactive Emacs Lisp functions to reduce the amount of time I +need to be in a terminal. I'll use the /prefix ~“w-”~ for work stuff/. Example +tasks: ++ Start/stop my servers ++ Interactively select an app to be opened in the browser ++ Do database migrations/rollbacks + +⟨ Obfuscated with lorem ipsum text. ⟩ +[[file:images/services-dashboard.png]] + +-------------------------------------------------------------------------------- + +Not using this, a bit too verbose to setup for each service but, more +accurately, does not Just Workᵀᴹ for my needs. +#+begin_src emacs-lisp +;; “M-x prodigy”, then press “s” to start a service; “S” to stop it; “$” to see it; “r”estart +(use-package prodigy :disabled t) + ;; C-h v prodigy-services ⇒ See possible properties. +#+end_src + +*** my/defaliases +:PROPERTIES: +:CUSTOM_ID: my-defaliases +:END: +#+begin_src emacs-lisp +(defalias 'defaliases 'my/defaliases) +(defmacro my/defaliases (src &rest tgts) + "Provide names TGTS as synonymous aliases for SRC, for discovarability. + +Often a function SRC can be construed from different perspectives, names, purposes TGTS. +Another example is when I define things with the ‘my/’ prefix, but also want to use them without. + +Example use: (my/defaliases view-hello-file greet-others learn-about-the-world) + +In particular: (my/defaliases OLD NEW) ≈ (defalias 'NEW 'OLD)." + `(--map (eval (quote (defalias `,it (quote ,src)))) (quote ,tgts))) +#+end_src + +*** Making unkillable buffers & shells +:PROPERTIES: +:CUSTOM_ID: Making-unkillable-buffers-shells +:END: +#+begin_details "Making unkillable buffers & shells" +#+begin_src emacs-lisp +(defun my/declare-unkillable-buffer (name) + (add-hook 'kill-buffer-query-functions + `(lambda () (or (not (equal (buffer-name) ,name)) + (progn (message "Not allowed to kill %s, burying instead; otherwise use “M-x force-kill”" (buffer-name)) + (bury-buffer)))))) + +(my/defaliases my/force-kill force-kill w-force-kill) +(cl-defun my/force-kill (&optional buffer-name) + (interactive) + (-let [kill-buffer-query-functions nil] + (if buffer-name + (kill-buffer buffer-name) + (kill-current-buffer)) + (ignore-errors (delete-window)))) + +(cl-defun my/run-unkillable-shell (command &optional (buffer-name command)) + "Example use: (my/run-unkillable-shell \"cd ~/my-noejds-project; npm run dev\" \"my-nodejs-project\")" + (-let [it (get-buffer buffer-name)] + (if it + (switch-to-buffer-other-window it) + (async-shell-command command buffer-name) + (my/declare-unkillable-buffer buffer-name)))) +#+end_src +#+end_details + +*** my/work-links +:PROPERTIES: +:CUSTOM_ID: my-work-links +:END: + +In my private ~work.el~ file, I have declarations of the form ~(my/work-links +"REPO" "https://⟨COMPANY⟩.atlassian.net/browse/REPO-%s")~ so that I can write +things like ~REPO:1234~ to get a nice green bold link in Org-mode that will take +me to that Jira link. I also have similar links to take me to the Github +repositories, backlogs, and Kanban boards. + +#+begin_src emacs-lisp + (cl-defmacro my/work-links (type url &optional (export-display '(format "%s-%s" type label))) + "Given a link of TYPE with a URL, produce the correct org-link. + + EXPORT-DISPLAY is string-valued term that may mention the symbolic names ‘type’ and ‘label’. + This is how the link looks upon export." + `(org-link-set-parameters + ,type + :follow (lambda (label) (browse-url (format ,url label))) + :export (lambda (label description backend) + (-let [full-url (format ,url label)] + (pcase backend + ('html (format "%s" full-url (-let [type ,type] ,export-display))) + ('latex (format "\\href{%s}{FM-%s}" full-url label)) + (_ full-url)))) + :face '(:foreground "green" :weight bold + :underline "blue" :overline "blue"))) + #+end_src + +*** w-start/stop-services +:PROPERTIES: +:CUSTOM_ID: w-start-stop-services +:END: +#+begin_src emacs-lisp +(defvar my/services nil "List of all services defined; used with `w-start-services' and `w-stop-services'.") + +(defun w-start-services () + (interactive) + (cl-loop for 𝑺 in my/services + do (funcall (intern (format "w-start-%s" 𝑺))))) + +(defun w-stop-services () + (interactive) + (cl-loop for 𝑺 in my/services + do (funcall (intern (format "w-stop-%s" 𝑺))))) +#+end_src + +*** w-status-of-services +:PROPERTIES: +:CUSTOM_ID: w-status-of-services +:END: + +#+begin_src emacs-lisp +;; It takes about ~3 seconds to build the Status of Services page, so let's jump to it if it's already built, and the user/me can request a refresh, if need be. +(global-set-key (kbd "M-S-SPC") + (lambda () (interactive) + (-let [buf (get-buffer "Status of Services")] (if buf (switch-to-buffer buf) (w-status-of-services))))) +;; +;; Since M-S-SPC brings up the transient menu, and most commands close the status buffer or are transient, we get the perception that the transient menu is "sticky"; i.e., stuck to the buffer, even though this is not true. I do not yet know how to make a transient menu stuck to a buffer. +;; +#+end_src + +#+begin_src emacs-lisp +(defun w-status-of-services () "Show me status of all servers, including their current git branch, and most recent emitted output." (interactive) (defvar w-status-of-services/branch-name-width 12 @@ -10114,7 +10397,7 @@ This is so sweet at work (and possibly at home!): From anywhere, #+begin_src emacs-lisp ;; More info & key bindings: https://docs.projectile.mx/projectile/usage.html (use-package projectile - :defer nil + :config (projectile-mode +1) (define-key projectile-mode-map (kbd "C-x p") 'projectile-command-map) @@ -10128,7 +10411,7 @@ This is so sweet at work (and possibly at home!): From anywhere, (setq projectile-enable-caching t)) (use-package projectile - :defer nil + :config (define-key projectile-mode-map (kbd "C-x p s") ;; I prefer helm-do-grep-ag since it shows me a live search @@ -10158,7 +10441,7 @@ Let's get the file path of the current file. We bind it and make it a top-level ;; https://cestlaz.github.io/posts/using-emacs-33-projectile-jump/ ;; https://github.com/bbatsov/projectile (use-package projectile - :defer nil + :config (projectile-global-mode) (define-key projectile-mode-map (kbd "s-p") 'projectile-command-map)) #+end_src @@ -10225,249 +10508,80 @@ check buffers; which must be installed separately. :defer nil :init (global-flycheck-mode) :config ;; There may be multiple tools; I have GHC not Stack, so let's avoid that. - (setq-default flycheck-disabled-checkers '(haskell-stack-ghc emacs-lisp-checkdoc)) - :custom (flycheck-display-errors-delay .3)) -#+end_src - -In an org-src block, we press ~C-c '~ to get into the language's mode where -flycheck will provide warnings. -#+begin_src haskell :tangle no -module Main where - -main :: IO () -main = putStrLn $ "nice" ++ f 0 - -f :: Int -> String -f x = x -- show x --- type error -#+end_src - -In-general, flycheck is intended for self-contained raw code ---not for source -blocks in Org-mode. Whence, the above example is a complete Haskell program, -with a named module and ~main~ method. - -I think the built-in [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Flymake.html][flymake]] syntax checker is better for Emacs Lisp, -so let's use that for ELisp. -#+BEGIN_SRC emacs-lisp -(use-package flymake - :defer nil - :hook ((emacs-lisp-mode . (lambda () (flycheck-mode -1))) - (emacs-lisp-mode . flymake-mode)) - :bind (:map flymake-mode-map - ("C-c ! n" . flymake-goto-next-error) - ("C-c ! p" . flymake-goto-prev-error))) -#+END_SRC - -Try it out: -#+begin_src emacs-lisp :tangle no -(setq 1 2) ;; Error: ‘1’ is not a variable. -#+end_src - -** COMMENT Jumping to definitions & references -:PROPERTIES: -:CUSTOM_ID: Jumping-to-definitions-references -:END: - -Out-of-the-box Emacs has ‘xref’ utilities ~M-.~ and ~C-u M-.~ to [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Xref.html#Xref][Find Identifier -References]]; however, tags to source definitions need to be generated using the -=etags= program. Nonetheless, the xref utilites are impressive and some just work: -For example, ~M-?~ cleverly finds /all/ references for an identifier in ‘near by’ -files; whereas ~C-u M-. RET my/.*~, for example, uses the given regular expression -to list all identifiers with prefix ~my/~, thereby listing my personally defined -names ^_^ - -| =C-M-. 𝓇𝓮ℊ𝓮𝓍= | Find all identifiers whose name matches the given pattern | - -Let's get [[https://github.com/jacktasia/dumb-jump][dumb-jump]], where the ‘dumb’ is possibly due to the fact -that it works by brute-force regular-expression lookup of -pre-defined ‘definitional template’ rules. It “just works” ^_^ -#+BEGIN_SRC emacs-lisp -(use-package dumb-jump - :defer nil - :bind (("M-g q" . dumb-jump-quick-look) ;; Show me in a tooltip. - ("M-g ." . dumb-jump-go-other-window) - ("M-g b" . dumb-jump-back) - ("M-g p" . dumb-jump-go-prompt) - ("M-g a" . xref-find-apropos)) ;; aka C-M-. - :config - ;; If source file is visible, just shift focus to it. - (setq dumb-jump-use-visible-window t)) -#+END_SRC - -In Lisp, for binding macros, it lists all possible mentions of the bound -variable ---the first is likely what is desired. Alternatively, one could just -add the necessary rule to the variable =dumb-jump-find-rules=. Otherwise, it -works fine even for locally bound definitions. It works depending on the -extension of a file. -** TODO COMMENT   ~M-x jsdoc~: Insert JSDocs with minimal type inference, useful with LSP-mode -:PROPERTIES: -:CUSTOM_ID: COMMENT-M-x-jsdoc-Insert-JSDocs-with-minimal-type-inference-useful-with-LSP-mode -:END: - -Let's make use of JSDocs for functions ---since then LSP will show us the types -and function description in a neato pop-up to the side. [[https://github.com/isamert/jsdoc.el][jsdoc.el]] inserts JSDoc -function comments/typedefs easily; it also tries to infer types by itself while -doing that. -#+begin_src emacs-lisp -;; [Minimal Type Inference] When default values are provided, then we can infer -;; the type of the arguments. -;; -;; Use: Run “M-x jsdoc” on a JS function. -;; -(use-package jsdoc - :defer nil - :quelpa (jsdoc - :fetcher github - :repo "isamert/jsdoc.el") - :config - (use-package tree-sitter) ;; Required dependencies - (use-package tree-sitter-langs) - :hook (js-mode . tree-sitter-mode)) -#+end_src - -** COMMENT Documentation Pop-Ups -:PROPERTIES: -:CUSTOM_ID: Documentation-Pop-Ups -:END: - -Let [[https://github.com/expez/company-quickhelp][documentation pop-up]] when we pause on a completion. -This is very useful when editing in a particular coding language, say via -~C-c '~ for org-src blocks. Or when working in a language-specific buffer. -#+BEGIN_SRC emacs-lisp -(use-package company-quickhelp - :defer nil - :config - (setq company-quickhelp-delay 0.1) - (company-quickhelp-mode) - ;; Especially when learning a new language, looking up its definition/docstring can be helpful. - ;; Note: I use “M-!” everywhere else to mean “define word at point”. - (bind-key "M-!" #'company-show-doc-buffer 'prog-mode-map)) -#+END_SRC - -This ~M-!~ is from plain ~company~, but I wanted to define it here beside the -quickhelp setup since they're related utilities. - -** COMMENT Turbolog: What's the value of this expression? :JavaScript: -:PROPERTIES: -:CUSTOM_ID: Turbolog-What's-the-value-of-this-expression -:END: -With [[https://github.com/Artawower/turbo-log][turbo-log]], I can select an expression then press [[kbd:C-x l l]] to have -that expression be part of a console log message, in the next line, that also -mentions the line number and buffer's name. I can toggle all these inserted -messages to be comments with kbd:C-x_l_c and kbd:C-x_l_u, and when I'm done -debugging I can quickly get rid of all of them with kbd:C-x_l_d. - -( Before TurboLog, I used a snippet bound to kbd:l_l that inserted -#+begin_src js :tangle no -console.log("%c ******* LOOK HERE *******", "color: green; font-weight: bold;"); -console.log({ List the variables here whose values you want to log }); -#+end_src -) - -*** COMMENT TODO: Move some of the stuff below to the TurboLog Github repo. :Does_not_export_to_HTML: -:PROPERTIES: -:CUSTOM_ID: COMMENT-TODO-Move-some-of-the-stuff-below-to-the-TurboLog-Github-repo -:END: - -#+begin_src emacs-lisp -(unless noninteractive - -(setq turbo-log--prefix "%c ******* LOOK HERE *******") -(defun length> (x y) (> (length x) y)) -(defun length= (x y) (= (length x) y)) -(ignore-error (use-package turbo-log - :defer nil - :quelpa (turbo-log :fetcher github :repo "artawower/turbo-log.el") - :config (setq turbo-console--prefix "✰"))) -(bind-key* "C-x l" #'my/turbo-log-hydra/body) -(defhydra my/turbo-log-hydra (:color blue :hint nil) - ("l" turbo-log-print "Log selected expression" :column "TurboLog: Insert meaningful log message for selected expressions") - ("c" turbo-log-comment-all-logs "Comment out all logs") - ("u" turbo-log-uncomment-all-logs "Uncomment out all logs") - ("d" turbo-log-delete-all-logs "Delete all TurboLog logs") - ("q" nil "Cancel")) + (setq-default flycheck-disabled-checkers '(haskell-stack-ghc emacs-lisp-checkdoc)) + :custom (flycheck-display-errors-delay .3)) +#+end_src +In an org-src block, we press ~C-c '~ to get into the language's mode where +flycheck will provide warnings. +#+begin_src haskell :tangle no +module Main where -(defun turbo-log--ecmascript-print (current-line-number formatted-selected-text prev-line-text multiple-logger-p) - "Console log for ecmascript, js/ts modes. -CURRENT-LINE-NUMBER - line number under cursor -FORMATTED-SELECTED-TEXT - formatted text without space at start position -PREV-LINE-TEXT - text from previous line -MULTIPLE-LOGGER-P - should guess list of available loggers?" +main :: IO () +main = putStrLn $ "nice" ++ f 0 - (let* ((is-empty-body (turbo-log--ecmascript-empty-body-p (turbo-log--get-line-text current-line-number))) - (insert-line-number (turbo-log--ecmascript-find-insert-pos current-line-number prev-line-text)) - (meta-info (turbo-log--format-meta-info insert-line-number)) - (normalized-code (turbo-log--ecmascript-normilize-code formatted-selected-text)) - (turbo-log--message - (concat - (turbo-log--choose-logger turbo-log--ecmascript-loggers multiple-logger-p) - "('" - meta-info - formatted-selected-text ": ', " - ;; HACK this is my change: Does the TurboLog prefix have a %c console styling marker? If so, use it. - (if (s-contains? "%c" meta-info) "'color: green; font-weight: bold;', " "") - normalized-code ")" - (if (plist-get turbo-log--ecmascript-configs :include-semicolon) ";")))) +f :: Int -> String +f x = x -- show x +-- type error +#+end_src - (if is-empty-body - (progn - (turbo-log--goto-line (- current-line-number 1)) - (beginning-of-line) - (search-forward-regexp "}[[:blank:]]*") - (replace-match "") - (turbo-log--insert-with-indent current-line-number turbo-log--message) - (turbo-log--insert-with-indent (+ current-line-number 1) "}") - (indent-according-to-mode)) - (turbo-log--insert-with-indent insert-line-number turbo-log--message)))) +In-general, flycheck is intended for self-contained raw code ---not for source +blocks in Org-mode. Whence, the above example is a complete Haskell program, +with a named module and ~main~ method. -) +I think the built-in [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Flymake.html][flymake]] syntax checker is better for Emacs Lisp, +so let's use that for ELisp. +#+BEGIN_SRC emacs-lisp +(use-package flymake + :defer nil + :hook ((emacs-lisp-mode . (lambda () (flycheck-mode -1))) + (emacs-lisp-mode . flymake-mode)) + :bind (:map flymake-mode-map + ("C-c ! n" . flymake-goto-next-error) + ("C-c ! p" . flymake-goto-prev-error))) +#+END_SRC + +Try it out: +#+begin_src emacs-lisp :tangle no +(setq 1 2) ;; Error: ‘1’ is not a variable. #+end_src -*** TODO ll-debug +** COMMENT Jumping to definitions & references :PROPERTIES: -:CUSTOM_ID: ll-debug +:CUSTOM_ID: Jumping-to-definitions-references :END: -# ~ Useful when no debugger setup - -[[https://melpa.org/#/ll-debug][ll-debug.el]] provides commands to support a low level debug style. -It features quick insertion of various debug output statements and -improved functions for commenting and uncommenting chunks of code. -#+begin_quote -I don't use debuggers very much. I know they can be a big help in some -situations and I tried some of them, but I find it almost always more -direct/convenient/enlightening to put a quick 'printf' into a critical area to -see what is happening than to fire up a big clumsy extra program where it takes -me ages just to step through to the interesting point. In order to avoid -repeated typing of 'printf("I AM HERE\n");' and similar stuff, I created -`ll-debug-insert'. It inserts a statement into your sourcecode that will display -a debug message. It generates unique messages on each invocation (the message -consists of a big fat DEBUG together with a counter and the current filename). +Out-of-the-box Emacs has ‘xref’ utilities ~M-.~ and ~C-u M-.~ to [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Xref.html#Xref][Find Identifier +References]]; however, tags to source definitions need to be generated using the +=etags= program. Nonetheless, the xref utilites are impressive and some just work: +For example, ~M-?~ cleverly finds /all/ references for an identifier in ‘near by’ +files; whereas ~C-u M-. RET my/.*~, for example, uses the given regular expression +to list all identifiers with prefix ~my/~, thereby listing my personally defined +names ^_^ ----ll-debug.el -#+end_quote +| =C-M-. 𝓇𝓮ℊ𝓮𝓍= | Find all identifiers whose name matches the given pattern | -#+begin_src emacs-lisp -;; C-u C-v C-d ⇒ Log a message, printing values of expressions. -;; E.g., in JS this prints, console.log("DEBUG-5-del.js"," 1 + 3:",1 + 3); -;; Note “5” is the fifth debug message, and “del.js” is the name of the buffer. -;; Works with Rust, Java, Lisps, JS, TS, Clojure, C/C++, Ruby, Matlab/Octave, Shell, Perl. -(use-package ll-debug - :defer nil +Let's get [[https://github.com/jacktasia/dumb-jump][dumb-jump]], where the ‘dumb’ is possibly due to the fact +that it works by brute-force regular-expression lookup of +pre-defined ‘definitional template’ rules. It “just works” ^_^ +#+BEGIN_SRC emacs-lisp +(use-package dumb-jump + :defer nil + :bind (("M-g q" . dumb-jump-quick-look) ;; Show me in a tooltip. + ("M-g ." . dumb-jump-go-other-window) + ("M-g b" . dumb-jump-back) + ("M-g p" . dumb-jump-go-prompt) + ("M-g a" . xref-find-apropos)) ;; aka C-M-. :config - (bind-key "C-x l" (lambda () (interactive) (ll-debug-insert 1)) #'prog-mode-map)) - -;; See variable `ll-debug-statement-alist' if you want to know which -;; modes are currently supported by ll-debug. You can add new modes -;; with `ll-debug-register-mode'. -;; -;; If you want to get rid of the debug messages, use -;; `ll-debug-revert'. It finds and removes the lines with the debug -;; output statements, asking for confirmation before it removes -;; anything. -#+end_src + ;; If source file is visible, just shift focus to it. + (setq dumb-jump-use-visible-window t)) +#+END_SRC +In Lisp, for binding macros, it lists all possible mentions of the bound +variable ---the first is likely what is desired. Alternatively, one could just +add the necessary rule to the variable =dumb-jump-find-rules=. Otherwise, it +works fine even for locally bound definitions. It works depending on the +extension of a file. ** COMMENT Which function are we writing? :PROPERTIES: :CUSTOM_ID: Which-function-are-we-writing @@ -10588,7 +10702,7 @@ For Emacs, [[https://github.com/ankurdave/color-identifiers-mode][Color Identifi #+begin_src emacs-lisp (use-package color-identifiers-mode - :defer nil + :config (global-color-identifiers-mode)) ;; Sometimes just invoke: M-x color-identifiers:refresh @@ -10611,7 +10725,7 @@ Here are further reads: - [[https://github.com/jacksonrayhamilton/context-coloring][Colouring by Context]] ---an Emacs package - [[http://www.linusakesson.net/programming/syntaxhighlighting/][A case against syntax highlighting]] -** TODO COMMENT Text Folding ---Selectively displaying portions of a program :causes_HIDING_ALL_BLOCKS_issue: +** TODO Text Folding ---Selectively displaying portions of a program :causes_HIDING_ALL_BLOCKS_issue: :PROPERTIES: :CUSTOM_ID: Text-Folding :END: @@ -10663,7 +10777,7 @@ to a programming language's syntactic constructions. #+begin_src emacs-lisp (use-package vimish-fold - :defer nil + :config (vimish-fold-global-mode 1)) #+end_src #+end_box @@ -10982,12 +11096,7 @@ The [[https://github.com/killdash9/buffer-flip.el][docs]], for the following, ha See [[https://www.emacswiki.org/emacs/buffer-move.el][buffer-move]] if you're interested in moving the buffers, and their windows, into new configurations. -** COMMENT Shell / Terminal :relocate: -:PROPERTIES: -:CUSTOM_ID: Shell-Terminal -:END: - -*** hr: [[https://github.com/LuRsT/hr][A horizontal for your terminal]] +** hr: [[https://github.com/LuRsT/hr][A horizontal for your terminal]] :relocate: :PROPERTIES: :CUSTOM_ID: hr-https-github-com-LuRsT-hr-A-horizontal-for-your-terminal :END: @@ -11004,62 +11113,7 @@ $i^{th}$-line having pattern =pᵢ=. (system-packages-ensure "hr") ;; ≈ brew install hr #+end_src -** COMMENT Github Browser Extensions (for Chrome) -:PROPERTIES: -:CUSTOM_ID: Github-Browser-Extensions-for-Chrome -:END: - -+ [[https://github.com/ovity/octotree][octotree]] :: Lets you explore the files and folders of a repository with a - tree and search bar. Can also quickly browse the files changed in the current - PR/branch. - -+ [[https://github.com/jasonlong/isometric-contributions][isometric-contributions]] :: Renders your contribution graph on your profile in - a nicer way: 3D instead of the default 2D. - -+ [[https://github.com/OctoLinker/OctoLinker][OctoLinker]] :: Turns language-specific statements like ~include~, ~require~ or - ~import~ into links. Gives you the ability to go to files by clicking at their - paths, not only internal files it also works for imported packages! - -+ [[https://github.com/Justineo/github-hovercard][github-hovercard]] :: Show a neat hover card when you hover over an element! No - need to open everything in a new page, just hover over it! - -+ [[https://github.com/homerchen19/github-file-icons][github-file-icons]] :: Gives different filetypes different icons to GitHub 🚀 - -+ [[https://github.com/N1ck/gifs-for-github][gifs-for-github]] :: Makes it easy to search GIPHY and add a GIF into any GitHub comment box. - -+ [[https://github.com/benbalter/github-mention-highlighter][github-mention-highlighter]] :: Any GitHub issue you've been mentioned in and your mentions should now be more easily visible. - -+ :flame: [[https://github.com/sindresorhus/refined-github][refined-github]] :: Browser extension that simplifies the GitHub interface and adds useful features - - - Makes whitespace characters visible - - Adds one-click merge conflict fixers - - Adds reaction avatars showing who reacted to a comment - - Linkifies issue/PR references and URLs in code and conversation titles - - Adds a button to revert all the changes to a file in a PR - - Lets you hide every event except comments or unresolved comments in issues and PRs - - 🔥 Adds a build/CI status icon next to the repo’s name. - - Adds a button to download entire folders, via download-directory.github.io. - - Adds a link to preview HTML files. - - Adds a button to copy a file’s content. - - Shows PRs that touch the current file. - - [[https://user-images.githubusercontent.com/1402241/33802977-beb8497c-ddbf-11e7-899c-698d89298de4.gif][Enables tab and shift tab for indentation in comment fields.]] - - [[https://user-images.githubusercontent.com/1402241/53678019-0c721280-3cf4-11e9-9c24-4d11a697f67c.png][Adds a button to insert collapsible content (via
)]] - # ‼ - [[https://user-images.githubusercontent.com/1402241/65020298-1f2dfb00-d957-11e9-9a2a-1c0ceab8d9e0.gif][Wraps selected text when pressing one of Markdown symbols instead of replacing it: =[ ` ' " * ~ _= ]] - - [[https://user-images.githubusercontent.com/1402241/99895146-16b50c80-2c4d-11eb-8038-210e6fd5e798.png][🔥 Highlights the most useful comment in conversations.]] - - [[https://user-images.githubusercontent.com/1402241/33474535-a814ee78-d6ad-11e7-8f08-a8b72799e376.png][Shows color-coded review counts in PR lists.]] - - [[https://user-images.githubusercontent.com/1402241/79343285-854f2080-7f2e-11ea-8d4c-a9dc163be9be.gif][Mark/unmark multiple files as “Viewed” in the PR Files tab. Click on the - first checkbox you want to mark/unmark and then shift-click another one; all - the files between the two checkboxes will be marked/unmarked as “Viewed”.]] - - [[https://user-images.githubusercontent.com/16872793/81943321-38ac4300-95c9-11ea-8543-0f4858174e1e.png][Shows the first Git tag a merged PR was included in.]] - - Automatically deletes the branch right after merging a PR, if possible. - - [[https://user-images.githubusercontent.com/16872793/87246205-ccf42400-c419-11ea-86d5-0e6570d99e6e.gif][Uses the first commit for a new PR’s title and description.]] - - [[https://user-images.githubusercontent.com/1402241/54178764-d1c96080-44d1-11e9-889c-734ffd2a602d.png][Adds one-click buttons to change diff style and to ignore the whitespace and a keyboard shortcut to ignore the whitespace: d w.]] - ( The toggle-whitespace-changes is very useful when there are minor linting changes that can be safely ignored in a PR. ) - - Adds a keyboard shortcut to visit your own profile: g m. - - [[https://user-images.githubusercontent.com/170270/55060505-31179b00-50a4-11e9-99a9-c3691ba38d66.png][Renders `text in backticks` in issue titles, commit titles and more places.]] - - Shows all of Refined GitHub’s new keyboard shortcuts in the help modal ([[kbd:?]] hotkey). - -** Browse remote files +** COMMENT Browse remote files :PROPERTIES: :CUSTOM_ID: Browse-remote-files :END: @@ -11074,4560 +11128,4804 @@ $i^{th}$-line having pattern =pᵢ=. ;; Usage: [Optionally select a region then] M-x browse-at-remote[-kill] (use-package browse-at-remote :defer nil) #+end_src -** A nice Emacs interface for a portion of the “gh” CLI -:PROPERTIES: -:CUSTOM_ID: A-nice-Emacs-interface-for-a-portion-of-the-gh-CLI -:END: -#+begin_src emacs-lisp -;; A nice Emacs interface for the a portion of the “gh” CLI. -(my/defaliases my/gh-checkout gh-checkout w-pr-checkout w-branch-checkout) -(cl-defun my/gh-checkout (&optional repo) - "With prefix, select a branch name; otherwise a Pull Request name. - -If no REPO is provided, let the user select one from a menu. -Example use: (w-pr-checkout \"~/my-repo\") - (w-pr-checkout)" - (interactive) - (let* ((repo (or repo (completing-read "Repo: " (projectile-relevant-known-projects)))) - (default-directory repo) ;; temporarily override this global variable, used with magit - (current-branch (magit-get-current-branch)) - (all-branches (magit-list-local-branch-names)) - (status (format "cd %s; gh pr status" repo))) - (if current-prefix-arg - (-let [branch (completing-read (format "New branch (Currently “%s”): " current-branch) all-branches)] - (shell-command-to-string (format "cd %s; git checkout %s" repo branch))) - (let* ((PR-list (s-split "\n" (shell-command-to-string (format "cd %s; gh pr list" repo)))) - (pr♯ (car (s-split "\t" (completing-read "PR: " PR-list)))) - (_ (shell-command-to-string (format "cd %s; gh pr checkout %s" repo pr♯))) - (new-branch (magit-get-current-branch))) - ;; Show nice status - (async-shell-command status) - (magit-status repo))))) -#+end_src - ** COMMENT [[https://github.com/sshaw/copy-as-format][copy-as-format:]] Emacs function to copy buffer locations as GitHub/Slack/JIRA etc... formatted code. :PROPERTIES: -:CUSTOM_ID: https-github-com-sshaw-copy-as-format-copy-as-format-Emacs-function-to-copy-buffer-locations-as-GitHub-Slack-JIRA-etc-formatted-code -:END: -#+begin_src emacs-lisp -;; Usage: [C-u] M-x copy-as-format ⇒ Copies selected region, or current line. -;; Also use: copy-as-format-𝒮, to format to a particular 𝒮tyle. -;; Without suffix 𝒮, format defaults to `copy-as-format-default`. -;; With a prefix argument prompt for the format style 𝒮. -;; Easy to add more formats. -(use-package copy-as-format :defer nil) -#+end_src - -** See all company related PRs -:PROPERTIES: -:CUSTOM_ID: See-all-company-related-PRs -:END: - -#+begin_src emacs-lisp -(cl-defun w-PRs (&rest query-options) - "See all company related PRs" - (interactive) - (thread-last `("is:open" "is:pr" "archived:false" "draft:false" ,@work/gh-tags ,@query-options) - (mapcar #'url-hexify-string) - (s-join "+") - (concat "https://github.com/pulls?q=") - browse-url)) -;; -(cl-loop for (name . query-options) - in `((month ,(format-time-string "updated:>=%Y-%m-01")) - (today ,(format-time-string "updated:>=%Y-%m-%d")) - (created-this-week ,(format "created:>=%s" - (org-read-date nil nil "++1" nil (org-read-date nil t "-sun")))) ;; Date of most recent Monday - (stale!! ,(format "updated:<=%s" (org-read-date nil nil "-1w"))) ;; Items not touched in over a week - (mentions-me "mentions:alhassy") ;; i.e., stuff I need to look at - (involves-me "involves:alhassy") - (process-manager "label:\"quick and easy\"" "repo:process-builder") - (newts "label:\"Newts Priority Review\",Newts")) - do (eval `(cl-defun ,(intern (format "w-PRs-%s" name)) () (interactive) (w-PRs ,@query-options)))) +:CUSTOM_ID: https-github-com-sshaw-copy-as-format-copy-as-format-Emacs-function-to-copy-buffer-locations-as-GitHub-Slack-JIRA-etc-formatted-code +:END: +#+begin_src emacs-lisp +;; Usage: [C-u] M-x copy-as-format ⇒ Copies selected region, or current line. +;; Also use: copy-as-format-𝒮, to format to a particular 𝒮tyle. +;; Without suffix 𝒮, format defaults to `copy-as-format-default`. +;; With a prefix argument prompt for the format style 𝒮. +;; Easy to add more formats. +(use-package copy-as-format :defer nil) #+end_src -** TODO SQL ---via LSP +** w-screencapture :PROPERTIES: -:CUSTOM_ID: SQL-When-doing-serious-database-work-I-love-using-DBeaver-But-when-I-only +:CUSTOM_ID: w-screencapture :END: -When doing ‘serious’ database work, I love using DBeaver. But when I only want -to /quickly/ run a query, to check something, *without the fricition of switching -applications*, then doing so in Emacs is a no-brainer: I use doc : w-sql to produce -a query buffer, if one doesn't exist, then ~C-c C-c~ to send queries. (This also -serves as a nice *reference mechanism* for useful queries.) - -Let's use [[https://github.com/lighttiger2505/sqls][LSP for SQL]], to get -+ Neato tooltips on table definitions and column constraints, - - Write ~select * from my-table t~ then delete the ~*~ and enter ~t.~ to see all - valid columns; or just delete the ~*~ and press ~.~ to see them. -+ Completions for tables & column names, -+ ~C-c C-c~ to execute query at point [My personal setup below], - - TODO: Run all queries with doc : lsp-execute-code-action -+ TODO: When sharing with others, maybe execute doc : lsp-format-buffer -+ Note: The tokenizer is known to a bit buggy. - - E.g., ~select 1 + 2 as "Numerical, yeah!"~ will not be run due to the ‘!’. - #+begin_src emacs-lisp -;; Installation: go install github.com/lighttiger2505/sqls -(setq lsp-sqls-server "/Users/musa/go/bin/sqls") -(setq lsp-sqls-timeout 1) -(setq lsp-sqls-workspace-config-path nil) -;; https://emacs-lsp.github.io/lsp-mode/page/lsp-sqls/ -(setq work/sqls-connections nil)(setq work/sqls-connections - '("host=127.0.0.1 port=5432 user=SUPER_SECRET password=ALSO_SUPER_SECRET dbname=YET_AGAIN_SECRET sslmode=disable")) -(setq lsp-sqls-connections - ;; (--map `((driver . "postgresql") (dataSourceName . ,it)) work/sqls-connections)) - `(((driver . "postgresql") (dataSourceName . ,(car work/sqls-connections))))) -;; TODO: Remove this 'car'! -(add-hook 'sql-mode-hook 'lsp) +(bind-key "C-c s" + (cl-defun w-screencapture () + "Interactively capture screen and save to clipboard; then paste in Slack, etc, with ⌘-c. -(use-package org-modern :defer nil) -(defun my/execute-query-at-point () - "Execute query at point and make resulting table an Org table and modernise it" - (interactive) - (lsp-sql-execute-paragraph) - (other-window 1) (org-modern-global-mode) (org-mode) (read-only-mode -1) - (while (re-search-forward "^\+" nil t) (replace-match "|" nil t)) - (toggle-truncate-lines) - (beginning-of-buffer) (execute-kbd-macro (read-kbd-macro "")) - (read-only-mode) (other-window -1) - (local-set-key "q" (lambda () "Quit buffer" (interactive) (ignore-errors (kill-buffer-and-window))))) + After we run this command, we can swipe up on mousepad to select different desktops, then + click & drag to select portition of screen to capture. -;; TODO: FIXME: Debugger entered--Lisp error: (void-variable sql-mode-map) -;; (bind-key "C-c C-c" #'my/execute-query-at-point 'sql-mode-map) -;; (bind-key "C-c C-" #'my/execute-query-at-point 'sql-mode-map) + Captured screen is NOT saved to disk, only copied to clipboard. -(defun w-sql () - "Quickly run a SQL query, then dispose of the buffer when done. +In MacOs, ++ Command + Shift + 5 ⇒ Select screen record ++ Command + Shift + 4 ⇒ Selection Screenshot ++ Command + Shift + 3 ⇒ Screenshot -Uses the first connection available, to change connections -invoke M-x `lsp-sql-switch-connection'." - (interactive) - ;; LSP only works on files; not buffers; so I use this file. - (find-file "~/.emacs.d/scratch.sql") - (insert work/sql-queries) ;; docs and examples - (sql-mode) - (hs-minor-mode -1) ;; I don't want the above comments to be collapsed away. - (beginning-of-buffer)) +See: https://osxdaily.com/2011/08/11/take-screen-shots-terminal-mac-os-x" + (interactive) + (async-shell-command "screencapture -i -c"))) + +(cl-defun w-delete-all-screenshots () + "Delete all “Screen Shot ⋯” files in ~/Desktop." + (interactive) + (thread-last (shell-command-to-string "cd ~/Desktop; ls") + (s-split "\n") + (--filter (s-starts-with-p "Screen Shot" it)) + (--map (f-delete (format "~/Desktop/%s" it))))) #+end_src +** COMMENT Screencapturing the Current Emacs Frame +:PROPERTIES: +:CUSTOM_ID: Screencapturing-the-Current-Emacs-Frame +:END: -#+begin_details "Previous setup ~ ejc-sql [Disabled]" -- ❌ ~ejc-sql~ crashes with updates. -- ✔ I like that ejc-sql gives me M-. to jump to definitions of tables/functions/etc. - - “Go to definition”: [M-.] M-x ejc-describe-entity ⇒ Show source code for a stored view, function, table, etc. - - Get entity definition: show creation SQL of view, package, function, procedure or type. - - C-c e t ⇒ See list of tables -- ✔ ejc works with org-mode out of the box; https://github.com/kostafey/ejc-sql#use-with-org-mode +Sometimes an image can be tremendously convincing, or at least sufficiently +inviting. The following incantation is written for MacOS and uses it's native +=screencapture= utility, as well as =magick=. +#+BEGIN_SRC emacs-lisp +(defun my/capture-emacs-frame (&optional prefix output) +"Insert a link to a screenshot of the current Emacs frame. -⇒ BEST OF BOTH WORLDS? ⇐ -(setq ejc-sql-separator "-- /") ;; Otherwise LSP sql server sees ‘/’ as a syntax error. +Unless the name of the OUTPUT file is provided, read it from the +user. If PREFIX is provided, let the user select a portion of the screen." +(interactive "p") +(defvar my/emacs-window-id + (s-collapse-whitespace (shell-command-to-string "osascript -e 'tell app \"Emacs\" to id of window 1'")) + "The window ID of the current Emacs frame. -#+begin_src emacs-lisp :tangle no -;; (system-packages-ensure "leiningen") -;; NOTE: You must commented out the (require 'ejc-direx) from ejc-sql.el -;; Reason: https://github.com/kostafey/ejc-sql/issues/163 -(use-package ejc-sql - :defer nil - :config - (require 'ejc-company) - (push 'ejc-company-backend company-backends) - (setq ejc-completion-system 'standard) ;; Use my setup; i.e., Helm. - ;; [C-u] C-c C-c ⇒ Evaluate current query [With JSON PP]. - (bind-key "C-c C-c" - (lambda () (interactive) - (setq ejc-sql-complete-query-hook - (if current-prefix-arg - '(w-ejc-result-pp-json) ;; Defined below - '((lambda () ;; Give each line of text just one screen line. - (switch-to-buffer-other-window "*ejc-sql-output*") - (visual-line-mode -1) - (toggle-truncate-lines) - (other-window -1))))) - (ejc-eval-user-sql-at-point)) - 'sql-mode-map)) + Takes a second to compute, whence a defvar.") -(defun w-sql () - "Quickly run a SQL query, then dispose of the buffer when done. +(let* ((screen (if prefix "-i" (concat "-l" my/emacs-window-id))) + (temp (format "emacs_temp_%s.png" (random))) + (default (format-time-string "emacs-%m-%d-%Y-%H:%M:%S.png"))) +;; Get output file name + (unless output + (setq output (read-string (format "Emacs screenshot filename (%s): " default))) + (when (s-blank-p output) (setq output default))) +;; Clear minibuffer before capturing screen or prompt user +(message (if prefix "Please select region for capture …" "♥‿♥")) +;; Capture current screen and resize +(thread-first + (format "screencapture -T 2 %s %s" screen temp) + (concat "; magick convert -resize 60% " temp " " output) + (shell-command)) +(f-delete temp) +;; Insert a link to the image and reload inline images. +(insert (concat "[[file:" output "]]"))) +(org-display-inline-images nil t)) -By default uses a connection named “xxxx”, to see a list of -other connections call with a prefix argument." - (interactive) +(bind-key* "C-c M-s" #'my/capture-emacs-frame) +#+END_SRC - ;; Get DB credentials. One does “M-x ejc-connect-interactive” once, then - ;; “M-x ejc-insert-connection-data” and paste that into your init; then - ;; “M-x ejc-connect” provides a completion of possible DBs to connect to. - (load-file "~/Desktop/work.el") - ;; For the following: Alternatively, we could make a new binding such as “C-c C-j” - ;; which temporarily adds to this hook, then calls - (add-to-list 'ejc-sql-complete-query-hook 'w-ejc-result-pp-json) ;; Defined below - (require 'ejc-sql) - (-let [connection-name (if current-prefix-arg (ejc-read-connection-name) "xxxx")] - (switch-to-buffer-other-window (format "*SQL/%s*" connection-name)) - (thread-last - `("\n\n/\n-- DOCS & EXAMPLES\n--" - "-- SQL queries should be seperated by “/”" - "-- [C-u] C-c C-c ⇒ Evaluate current query [With JSON PP]" - "-- In result window, TAB/RET to navigate the columns/rows." - "-- C-c e t ⇒ List all tables" - "-- C-h t ⇒ ‘H’elp for a ‘t’able" - "\nselect 1 + 2 as \"Numerical, yeah!\"" - "\n/\n" - "-- More examples of useful SQL queries I might need, but don't want to remember" - ;; I've moved them out to my private work.el file. - ,@work/sql-queries) - (s-join "\n") - insert) - (sql-mode) - (hs-minor-mode -1) ;; I don't want the above comments to be collapsed away. - (ejc-connect connection-name) - (beginning-of-buffer) - (message "Connecting to DB... please wait a moment"))) +Why this way? On MacOS, ImageMagick's =import= doesn't seem to work ---not at all +for me! Also, I dislike how large the resulting image is. As such, I'm using +MacOS's =screencapture= utility, which in-turn requires me to somehow obtain frame +IDs. Hence, the amount of work needed to make this happen on my system was most +simple if I just wrote it out myself rather than tweaking an existing system. -(defun w-ejc-result-pp-json () - "Pretty print JSON ejc-result buffer." - (interactive) - (ignore-errors - (switch-to-buffer-other-window "*ejc-sql-output*") - (beginning-of-buffer) - (re-search-forward "{") - (backward-char 1) - (delete-region (point-min) (point)) - (end-of-buffer) - (re-search-backward "|") - (kill-line) - (json-mode) - (json-pretty-print-buffer) - (other-window -1) - (message-box "hiya"))) -#+end_src ++ ~C-c C-x C-v~ ⇒ Toggle inline images! -;; Add minimal table autocomplete to SQL -(require 'ejc-company) -(push 'ejc-company-backend company-backends) -(add-hook 'ejc-sql-minor-mode-hook - (lambda () - (company-mode t))) -#+end_details +** Comment-boxes up to the fill-column ---or banner instead? +:PROPERTIES: +:CUSTOM_ID: Comment-boxes-up-to-the-fill-column +:END: -- *Postgres is the Emacs of Databases:* /It has extensions!/ E.g., It can be - extended to write stored functions in Ruby, Python, and [[https://plv8.github.io/#:~:text=for(var%20i%3D0%3B%20i%3Ckeys.length%3B%20i%2B%2B)%7B%0A%20%20%20%20%20%20%20%20o%5Bkeys%5Bi%5D%5D%20%3D%20vals%5Bi%5D%3B%0A%20%20%20%20%7D%0A%20%20%20%20return%20o%3B][in-particular - JavaScript]]. So neat! - - [[https://dzone.com/articles/why-postgresql-so-awesome][Why PostgreSQL is so Awesome - DZone Java]] - - [[https://www.2ndquadrant.com/en/blog/postgresql-is-the-worlds-best-database/][PostgreSQL is the worlds' best database - 2ndQuadrant | PostgreSQL]] - - [[https://www.craigkerstiens.com/2012/04/30/why-postgres/][Why Postgres - Craig Kerstiens]] +GIF: https://endlessparentheses.com/images/comment-box.gif + +#+begin_src emacs-lisp +(defun my/comment-box (b e) + "Draw a box comment around the region but arrange for the region +to extend to at least the fill column. Place the point after the +comment box. + +Source: http://irreal.org/blog/?p=374 + +To do fancy stuff like removing boxes, centering them, etc +see https://github.com/lewang/rebox2/blob/master/rebox2.el" + (interactive "r") + (let ((e (copy-marker e t))) + (goto-char b) + (end-of-line) + (insert-char ? (- fill-column (current-column))) + (comment-box b e 1) + (goto-char e) + (set-marker e nil))) +#+end_src + +A comment box sometimes increases the size of a file more than I'd like, or more than others on my dev team would like. +As such, let's try [[https://github.com/WJCFerguson/banner-comment][banner comments]]: +- GIF: https://github.com/WJCFerguson/banner-comment/blob/35d3315683d3f97605207691b77e9f447af18fe2/demo.png +#+begin_src emacs-lisp +(use-package banner-comment :defer nil) +#+end_src -** Docker +Pretty slick! + +** TODO COMMENT Auto-format on Save :Reenabled:Should_be_Disabled:Breaks_HTML_export_of_org_files_with_unicode_or_emojis: :PROPERTIES: -:CUSTOM_ID: Docker +:CUSTOM_ID: Auto-format-on-Save :END: -#+begin_src emacs-lisp -;; Usage: M-x docker [RET ?] -(use-package docker - :defer nil - :config - (my/defaliases docker-containers w-show-docker-containers)) - -(defun w-stop&remove-docker-containers () - (interactive) - (shell-command "docker stop $(docker ps -a -q)") - (shell-command "docker rm $(docker ps -a -q)")) -(defun w-postgres-status () - (interactive) - (display-message-or-buffer (s-replace "healthy" "🆙 healthy 🍏" (shell-command-to-string "docker ps -a | grep postgres")))) +[[https://github.com/lassik/emacs-format-all-the-code/tree/eb2a7fa6da15d23b57921218a36ac67d523e81f1][Auto-format source code in many languages with one command]] -(cl-defun w-kill-process-running-on-port (&optional (port (completing-read "Port: " '("3310" "80" "9000" "8000" "8080" "etc, whatever you want")))) - "We use ‘lsof’ to list open files; as in: lsof -i :3310 +c0 - The +c0 prints the full name of the command rather than truncating it. +Let's auto-format source code in many languages using the same command for all +languages, instead of learning a different Emacs package and formatting command +for each language. - We then find the PID and kill the process." - (interactive) - (-let [process (shell-command-to-string (format "lsof -i :%s +c0" 3310))] - (-let [pid (ignore-errors (cl-second (s-split " " (cl-second (s-split "\n" process)))))] - (shell-command (format "kill %s" pid)) - (message process)))) +Just do ~M-x format-all-buffer~ and it will try its best to do the right thing. To +auto-format code on save, use the minor mode format-all-mode. Better yet, we ask +for it to do so “on save”. +- You will need to install external programs to do the formatting. If + ~format-all-buffer~ can't find the right program, it will try to tell you how to + install it. +- An alternative Emacs tool is [[https://github.com/radian-software/apheleia][apheleia: 🌷 Run code formatter on buffer + contents without moving point, using RCS patches and dynamic programming.]] +#+begin_src emacs-lisp +(use-package format-all + :defer nil + ;; To enable format on save for most programming language buffers: + :hook (prog-mode . format-all-mode) + :config + ;; Please use the default formatters; I don't care too much. + (add-hook 'format-all-mode-hook 'format-all-ensure-formatter)) #+end_src -*** my/docker-stop -:PROPERTIES: -:CUSTOM_ID: my-docker-stop -:END: +[[https://prettier.io/docs/en/index.html][Prettier]] is an opinionated code formatter for numerous web related languages, +including JS, TS, HTML, CSS, JSON, Vue, Markdown, and YAML. + #+begin_src emacs-lisp -(defun my/docker-stop (ctr) - "Stop all containers that mention CTR in their name, image, command, or container id" - (thread-last (shell-command-to-string "docker ps -a") - (s-split "\n") - (--filter (s-contains-p ctr it)) - (--map (car (s-split " " it))) ;; Get docker container ids - (--map (shell-command (concat "docker stop " it))))) +;; For JavaScript prettification: It automatically inserts semicolons, forces newlines, inserts parens, etc. +;; Lots of redundant stuff, but stuff to make it easy to work with others. +(shell-command "npm install --global prettier") +;; Specific package to do only JS prettification: https://github.com/prettier/prettier-emacs #+end_src -** my/open-in-terminal '⌘ -:PROPERTIES: -:CUSTOM_ID: my-open-in-terminal-' -:END: -#+begin_src emacs-lisp -(defalias 'my/open-in-terminal '⌘) -(cl-defun ⌘ (&rest cmds) - "Run terminal commands CMDS in a new MacOS Terminal instance, and bring it to focus. +For example, in a NodeJS app make a file ~.prettierrc.json~ whose contents are +#+begin_src json :tangle no +{ + "singleQuote": true, + "arrowParens": "avoid", + "printWidth": 120, + "semi": false, + "trailingComma": "none" +} +#+end_src +Unfortunately, my current team prefers ~"semi": true~ ---which is understandable +for people not familar with how JavaScript does [[https://www.freecodecamp.org/news/lets-talk-about-semicolons-in-javascript-f1fe08ab4e53/][semicolon insertion]]. -Example: (⌘ \"echo hello\" \"echo world\") +( If you're having trouble getting prettier to work on save, consider using this +[[https://github.com/codesuki/add-node-modules-path][package]] which detects your node modules' path. ) -Useful for those cases where I have to interact with non-trivial ‘interactive terminal menus’." - (shell-command (format "osascript -e 'tell app \"Terminal\" to activate do script %s'" - (pp-to-string (s-join ";" cmds))))) +Anyhow, with these rules: +- I write =const f = (x) => "x" == x= then on save it becomes =const f = x => 'x' == x;= +- I write =let xs = [1, 2, 3,]= then on save it becomes =let xs = [1, 2, 3];= +- I write =foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());= + then nothing happens on save; but if you add one more argument, =, 12=, then it gets reformatted with each arg on one line. -;; (⌘ "echo hello" "echo world") -#+end_src +Notice that in the first example above, only minor syntactic changes were made; +nothing altering semantics or quality of code ---for that, we use a /linter/. +In particular, [[https://emacs-lsp.github.io/lsp-mode/page/lsp-eslint/][LSP ESLint]] when activated will show us two errors: It's almost always +better to use ~===~ instead of ~==~ and literals should be on the right side of the equality. +We can use ~M-x lsp-execute-code-action~ to get a list of actions that can be performed to fix this /quality/ problem. -** Check if application APP is currently running, in use. +** Searching Hydra :PROPERTIES: -:CUSTOM_ID: Check-if-application-APP-is-currently-running-in-use +:CUSTOM_ID: Searching-Hydra :END: #+begin_src emacs-lisp -(cl-defun my/application-running? (app) - "Check if application APP is currently running, in use." - (not (equal "0" (s-trim (shell-command-to-string (format "ps aux | grep -v grep | grep -ci %s" app)))))) -#+end_src +(my/defhydra "s-f" "\t\tLocate Everything" search + :Buffer + ;; find all the occurrences of a string, pull out the lines containing the string to another buffer where [F2] I can edit and save, + ("e" helm-swoop "Editable") + ;; Implicit Regex, colourful + ("c" swiper "Classic") -** LSP: Making Emacs into a generic full-featured programming IDE + :Project + ;; “:toggle ℰ”: ℰ is a Boolean expression that is evaluated to tell us whether the state is on-or-off + ("t" (lambda () (interactive)) "Ignore specs/jsons" + :toggle (let* ((with-hole "ag %s --line-numbers -S --color --nogroup %%s %%s %%s") ;; ≈ original value of ‘helm-grep-ag-command’ + (ignores "--ignore=\"*spec.js\" --ignore=\"*.json\" --ignore=\"*.json5\"") + (on (equal helm-grep-ag-command (format with-hole ignores)))) + (if on (progn (setq helm-grep-ag-command (format with-hole "")) nil) ;; ≈ turn off the toggle + (setq helm-grep-ag-command (format with-hole ignores))))) + ("f" (lambda () (interactive) (helm-do-grep-ag t)) "File type") + ("d" (lambda () (interactive) (-let [default-directory (read-directory-name "Where do you want to search? ")] (helm-do-grep-ag nil))) "Directory") + ("D" (lambda () (interactive) (-let [default-directory (read-directory-name "Where do you want to search? ")] (helm-do-grep-ag t))) "Directory & type")) +#+end_src +** TODO ⌘-e: Edit Everything in a separate buffer :PROPERTIES: -:CUSTOM_ID: LSP-Making-Emacs-into-a-generic-full-featured-programming-IDE +:CUSTOM_ID: e-Edit-Everything-in-a-separate-buffer :END: -(TLDR: LSP gives Emacs a uniform way to deal with programming languages, such as -highlighting and refactoring.) - -[[https://microsoft.github.io/language-server-protocol/][Language Server Protocol (LSP)]] was created by Microsoft to define a common -standard for providing editor-agnostic code intelligence support. It has become -widely popular since its creation. Emacs support for lsp comes with the [[https://emacs-lsp.github.io/lsp-mode/][lsp-mode]] -and the [[https://github.com/joaotavora/eglot][eglot]] (‘E’macs poly‘glot’) packages; let's use the first one since it -comes with all features enabled by default (which increases discoverability). -[ See also: [[https://emacs-lsp.github.io/lsp-mode/tutorials/how-to-turn-off/][A guide on disabling/enabling lsp-mode features - LSP Mode - LSP support for Emacs]]. ] +Edit comment/string/docstring/code block in separate buffer with your favourite +mode. -Essentially how it works is that LSP defines a bunch of useful requests ----such as code completion, types, etc--- and our editor (Emacs) makes these -requests to a server (a process that knows how to handle them) then we get the -results and show them nicely in our editor (Emacs). ++ ⌘-e to toggle “e”diting of thing at point. ++ ⌘-e will try to edit thing at point, or selection, if possible; otherwise it + will exit edit session. + - 🔥 Avoid escape nightmares by editing strings in a separate buffer. + - On a string, press ⌘-e and a new buffer pops up with unescaped content, + letting you edit raw strings directly. It then takes care of automatically + escaping strings for you when you press ⌘-e, or ~C-c C-c~ ---or discard your + changes with ~C-c C-k~. ++ ⌘-e can be used within edit session to create new edit sessions, if possible; then ⌘-e will “pop-off-the-stack” as expected. + - (I already use ⌘-e to toggle editing Org src blocks, and when editing a block ⌘-e will try to do a recursive edit if possible, or exit the edit session.) + - (The separedit package also let's me press ⌘-e to edit variable values when describing them, and to edit text when in the minibuffer.) -Further reading: -+ [[https://emacs-lsp.github.io/lsp-mode/page/main-features/][Main features - LSP Mode - LSP support for Emacs]] ⇒ Reasons (with gifs) to use - lsp-mode ;-) -+ [[https://emacsconf.org/2019/talks/19/][How Emacs became my awesome Java editing environment [EmacsConf2019]]] - is an excellent ~10min video demoing prime features of LSP. -+ [[https://www.toptal.com/javascript/language-server-protocol-tutorial][Using Text Editors for Programming with LSP | Toptal]] -+ [[https://microsoft.github.io//language-server-protocol/overviews/lsp/overview/][LSP overview]] -+ :flame: [[https://emacs-lsp.github.io/lsp-mode/tutorials/reactjs-tutorial/][React JavaScript Tutorial in Emacs - LSP Mode]] - Nice walkthrough on - setting-up LSP for JavaScript (along with ESlint) and trying it out. +From [[https://github.com/twlz0ne/separedit.el][source]]: +#+begin_example org ++----------+ Edit +-----------+ Edit +-----------+ +| | ---------------------> | edit | ---------------------> | edit | ... +| | point-at-comment? | buffer | point-at-comment? | buffer | +| source | point-at-string? | | point-at-string? | | ... +| buffer | point-at-codeblock? | (markdown | point-at-codeblock? | (markdown | ... +| | point-at-...? | orgmode | point-at-...? | orgmode | +| | <--------------------- | ...) | <--------------------- | ...) | ... ++----------+ Commit changes +-----------+ Commit changes +-----------+ +#+end_example -*Super simple setup for lsp-mode*: Install lsp-mode with [[kbd:][M-x package-install -⟨return⟩ lsp-mode]] then open, say, a JavaScript file and press . It -will ask you to download a server, such as ~jsts-ls~ for “JavaScript-TypeScript -Language Server”. /[You may need to restart Emacs; when you open a file, LSP -needs to know what ‘workspace’ it belongs to!]/ Now you can, for example, perform - to see all references of the name under the cursour, or to go to its definition, or to see its type (which already -shows in the mode line). In your JS file, write the errenous line ~let a == 1;~ -then with your cursour on that line you'll see some info to the side about the -error (thanks to ~lsp-ui-mode~) and if you invoke ~lsp-treemacs-errors-list~ -then you get a nice hierarchical, ⟨tab⟩-ful, list of errors. To find symbol -names, with autocompletion of all names in your workspace, use -(thanks to ~helm-lsp~), for the current file TODO. doc.lsp-ui-imenu (or more simply, -) is nice. As the LSP server runs it can even detect JSON objects -and show their values in the mode line when the cursour is above them; which can -also be requested with . (TODO. doc.lsp-describe-thing-at-point). /That -is all!/ +[In-general, we can invoke [[https://github.com/Fanael/edit-indirect/blob/e3d86416bcf8ddca951d7d112e57ad30c5f9a081/edit-indirect.el#L124][M-x edit-indirect-region]] to edit any selected piece +of text in its own buffer, then ~C-c C-c~ (or ~⌘-e~ with my setup) to commit the +edit. Actually, I've altered by ~⌘-e~ setup to also account for a selected region +😉] -| [[kbd:][s-l g g]] | Find definitions of the symbol under point | -| [[kbd:][s-l g r]] | Find references of the symbol under point | -| [[kbd:][s-l g t]] | Find type definitions of the symbol under point | -| [[kbd:][s-l g a]] | Find all meaningful symbols that match pattern (project wide) | -| [[kbd:][s-l h h]] | :fire: Display the type signature and documentation of the thing at point | -| [[kbd:][s-l h g]] | Trigger display hover information popup and hide it on next typing | -| [[kbd:][s-l r r]] | Rename the symbol (and all references to it) | +#+begin_src emacs-lisp +(use-package separedit :defer nil) +;; +;; # Example Usage +;; +;; 1. Press ⌘-e on this line, to edit this entire comment. +;; 2. Press ⌘-e to exit the edit session. +;; +;; Since my ⌘-e is context sensitive, to determine whether to continue editing or +;; exit; you can explicitly request an edit with C-c ' and an exit with C-c C-c. +;; +;; ``` +;; ;; 3. Press ⌘-e on this line, to edit this source block! +;; ;; 4. Press ⌘-e on this line, to edit this inner-most comment! +;; ;; 5. At start of next line, press “⌘-r ⌘-e” to edit just the source block +;; ;; +;; (cl-defun index (&rest args) +;; "6. Press ⌘-e to edit this string, \"7. and again in these quotes\"" +;; "

8. Press ⌘-e to edit this HTML block, in Web-mode

") +;; +;; ;; 9. Press C-u ⌘-e to guess the language of the next string (Rust); then ⌘-r C-c C-r to quickly run the code. +;; "fn main() { println!(\"{}\", \"hello!\"); }" +;; +;; ;; 10. Select & press “C-u ⌘-e” on the following, to edit it in whatever mode you want. +;; ;; select * from table -- Or just press ⌘-e and have the mode detected. +;; +;; ``` -Also: TODO. doc.lsp-format-buffer makes the code more readable. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Setup to make the above ⌘-e behaviour happen. -#+begin_src emacs-lisp -(use-package lsp-mode - :defer nil - :init - ;; Set prefix for lsp commands - ;; (setq lsp-keymap-prefix "s-l") ;; default - ;; Set how often highlights, lenses, links, etc will be refreshed while you type - ;; (setq lsp-idle-delay 0.500) ;; default - :hook ;; Every programming mode should enter & start LSP, with which-key support - (js-mode . lsp-mode) ;; Enter LSP mode - (js-mode . lsp) ;; Start LSP server - (lsp-mode . lsp-enable-which-key-integration) - ;; For some reason, my usual snippet setup does not work with LSP, so using “C-x y” - :bind ("C-x y" . #'yankpad-insert) - ;; When I'm typing and possible completitions appear, I can press M-! to see their docstrings in a temporary buffer. - ;; But on already written words, I'll use “C-u M-.” to toggle having their docstrings in a scrollable&growable tooltip-like-overlay-window. - ;; Use “bind*” to override Js-mode's dumbjump, which is useless for me - :bind* ("M-." . (lambda () (interactive) - (if (not current-prefix-arg) - (call-interactively #'lsp-ui-peek-find-definitions) - (if (lsp-ui-doc--visible-p) - (lsp-ui-doc-hide) - (lsp-ui-doc-show))))) - :commands lsp) +;; Make “⌘-e” toggle editing string literals / select region / [Org/markdown] code block / comment block when programming. +(--map (bind-key "s-e" #'separedit it) + '(prog-mode-map minibuffer-local-map help-mode-map)) ;; TODO: helpful-mode-map -;; If a server crashes, restart it without asking me. -(setq lsp-restart 'auto-restart) #+end_src #+begin_src emacs-lisp -;; https://emacs-lsp.github.io/lsp-mode/page/languages/ -;; M-x lsp-install-server ⟨return⟩ jsts-ls -;; M-x lsp-install-server ⟨return⟩ json-ls -;; M-x lsp-install-server ⟨return⟩ eslint -;; M-x lsp-install-server ⟨return⟩ css-ls -;; M-x lsp-install-server ⟨return⟩ html-ls +;; TODO:Merge these changes upstream + +;; I'm focusing on a specific region to edit, so let's not be distracted by anything else. +;; This makes the “editing stack” feel like a stack, with ⌘-e pushing new editing session buffers, +;; and C-c C-c, or ⌘-e on non-editable lines, to pop-off the stack. +;; (advice-add #'separedit :after (lambda (&rest _) (delete-other-windows))) +;; +;; NOTE: This actually breaks the stack nature of popping with ⌘-e; we need to actually save the stack via some list of buffers than push/pop buffers on that variable. + +;; I don't want to be bothered for what mode I'm in, when a region is selected using current major mode. +;; I'll use a prefix, “C-u ⌘-e”, if I want to select a mode for my current selected text. +(advice-add #'separedit--select-mode :before-until + (lambda (&rest _) + (when (and (not current-prefix-arg) (region-active-p)) (pp-to-string major-mode)))) + +;; Also: When on a string ∷ +(advice-add #'separedit--select-mode :before-until + (lambda (&rest _) + "When on a string ∷ ++ ⌘-e ⇒ Edit string at point ++ C-u ⌘-e ⇒ Auto-detect my string's major mode ++ C-u C-u ⌘-e ⇒ Let me select a major mode" + (-let [str? (ignore-errors (thing-at-point 'string))] + (case (car current-prefix-arg) + (4 (when str? (pp-to-string (my/detect-prog-mode str?)))) + (_ nil))))) + +;; NOTE: By default, separedit provides colouring for 'strings', "strings", and `strings' +;; This doesn't look very good when I have a single quote within double quotes: +;; In an Emacs Lisp buffer, editing the string "Bob's Work" gives unexpected highlighting. +;; ``` +;; (advice-add #'separedit :after +;; (lambda (&rest _) +;; (when (s-ends-with? "string-mode" (pp-to-string major-mode)) +;; (text-mode)))) +;; ``` #+end_src -For JavaScript development, actually install the required servers locally. -- Otherwise, tooltips with documentation on hover, args info echoed in mode - line, jump to definition, and etc wont work. #+begin_src emacs-lisp -(shell-command "npm i -g typescript-language-server; npm i -g typescript") +;; In the indirect buffer, make ⌘-e finish editing. +(use-package edit-indirect + :config (bind-key "s-e" + (lambda () + (interactive) + (or (ignore-errors (call-interactively #'separedit)) + (call-interactively #'edit-indirect-commit))) + #'edit-indirect-mode-map)) + +;; I also have “s-e” bound to `org-edit-src-exit'. +(advice-add 'org-edit-src-exit :before-until + (lambda (&rest r) + (when (ignore-errors (separedit)) t))) #+end_src #+begin_src emacs-lisp -;; lsp-ui for fancy sideline, popup documentation, VScode-like peek UI, etc. -;; https://emacs-lsp.github.io/lsp-ui/#intro -;; -;; You only have to put (use-package lsp-ui) in your config and the package will -;; work out of the box: By default, lsp-mode automatically activates lsp-ui. -(use-package lsp-ui :defer nil) +;; → ⌘-e on an Org paragraph pops-up an edit session in Org mode. +;; → ⌘-e on a selection in Org mode pops-up an edit session in Org mode. +;; TODO: Consider forming an alist for special blocks to refer to their preferred +;; edit mode, defaulting to Org-mode? Perhaps something to consider /after/ +;; addressing the bug below. +;; (advice-unadvice 'org-edit-special) MA: TODO: FIXME: Delete this? +(advice-add 'org-edit-special :around + (lambda (orginal-function &rest r) + (cond + ((region-active-p) (call-interactively #'edit-indirect-region) (org-mode)) + ((equal 'paragraph (car (org-element-at-point))) + (mark-paragraph) (call-interactively #'edit-indirect-region) (org-mode)) + (t (or (ignore-errors (apply orginal-function r)) + ;; We try to edit a special block when orginal-function fails. + ;; This way src blocks are not confused with the more generic idea of special blocks. + (when + (my/org-in-any-block-p) + ;; Note using org-element-at-point doesn't work well with special blocks when you're somewhere within the block. + ;; It only works correctly when you're on the boundary of the special block; which is not ideal. + ;; This is why I'm not using: (org-element-property :begin elem). + (-let [(start . end) (my/org-in-any-block-p)] + (set-mark-command start) + (goto-char end) (previous-line 2) (end-of-line) ;; FIXME: Still shows #+end_XXX for some reason. + (call-interactively #'edit-indirect-region) (org-mode)))))))) +#+end_src -;; lsp-treemacs for various tree based UI controls (symbols, errors overview, -;; call hierarchy, etc.) -(use-package lsp-treemacs :defer nil) ;; https://github.com/emacs-lsp/lsp-treemacs -;; M-x lsp-treemacs-errors-list +#+begin_src emacs-lisp +;; where... +(defun my/org-in-any-block-p () + "Return non-nil if the point is in any Org block. -;; helm-lsp provides “on type completion” alternative of cross-referencing. -;; https://github.com/emacs-lsp/helm-lsp -(use-package helm-lsp :defer nil) - (require 'lsp-mode) - (define-key lsp-mode-map [remap xref-find-apropos] #'helm-lsp-workspace-symbol) -;; Jump to a symbol's definition in the current workspace with “s-l g a” or “M-g -;; a” (The 'a' stands for apropos, which means appropriate nature) +The Org block can be *any*: src, example, verse, etc., even any +Org Special block. -;; Set the amount of data which Emacs reads from a process. -;; Some LSP responses are in the 8k-3MB range. -;; ⟦ 1 megabyte ≈ 1 million bytes ≈ 1 000 000 bytes ⟧ -;; NO! (setq read-process-output-max (* 1024 1024)) ;; ~1mb; [default 4k] -;; NO! (setq gc-cons-threshold (* 2 8 1000 1024)) ;;; ~16mb; default is: 800 000 -;; A large gc-cons-threshold will cause freezing and stuttering during long-term -;; interactive use. This one seems to be a good default. +This function is heavily adapted from `org-between-regexps-p'. + +Src: https://scripter.co/splitting-an-org-block-into-two/" + (save-match-data + (let ((pos (point)) + (case-fold-search t) + (block-begin-re "^[[:blank:]]*#\\+begin_\\(?1:.+?\\)\\(?: .*\\)*$") + (limit-up (save-excursion (outline-previous-heading))) + (limit-down (save-excursion (outline-next-heading))) + beg end) + (save-excursion + ;; Point is on a block when on BLOCK-BEGIN-RE or if + ;; BLOCK-BEGIN-RE can be found before it... + (and (or (org-in-regexp block-begin-re) + (re-search-backward block-begin-re limit-up :noerror)) + (setq beg (match-beginning 0)) + ;; ... and BLOCK-END-RE after it... + (let ((block-end-re (concat "^[[:blank:]]*#\\+end_" + (match-string-no-properties 1) + "\\( .*\\)*$"))) + (goto-char (match-end 0)) + (re-search-forward block-end-re limit-down :noerror)) + (> (setq end (match-end 0)) pos) + ;; ... without another BLOCK-BEGIN-RE in-between. + (goto-char (match-beginning 0)) + (not (re-search-backward block-begin-re (1+ beg) :noerror)) + ;; Return value. + (cons beg end)))))) #+end_src -:flame: If you leave your cursour on a name, all other occurances are -highlighted /and/ you get to see its (type) description in the mode line. +I'd like to [[https://github.com/andreasjansson/language-detection.el][guess the language]] I'm in, when working with strings. +#+begin_src emacs-lisp +(use-package language-detection :defer nil) +;; Usage: M-x language-detection-buffer ⇒ Get programming language of current buffer +;; Also, (language-detection-string "select * from t") ;; ⇒ sql -🔥 As you begin to type, all variable/function names /loosely/ matching what you -type will be shown, along with their (type) descriptions. +;; TODO: Push this upstream; https://github.com/andreasjansson/language-detection.el/issues/1 +(cl-defun my/detect-prog-mode (&optional string) + "Guess programming mode of the current buffer, or STRING if it is provided. -🚁 If you see an error ~“LSP :: Could not connect to 𝒳 server”~, then run -TODO. doc.lsp-doctor and look at the buffer ~𝒳::stderr~ for hints on what the cause -could have been; [[https://emacs-lsp.github.io/lsp-mode/page/faq/][FAQ]]. +When called interactively, it enables the mode; +from Lisp it just returns the name of the associated mode. -The Language Server protocol is used between a tool (the client) and a language -smartness provider (the server) to integrate features like auto complete, go to -definition, find all references and alike into the tool. + ;; Example Lisp usage + (call-interactively #'my/detect-prog-mode) -LSP, more or less, reduces the linear problem of language-specific-IDE into a -constant problem: Each language implements an LSP program which is then used by -an LSP-enabled editor to provide an IDE experience. +`language-detection-buffer' returns a string which is not always the name of the +associated major mode; that's what we aim to do here." + (interactive) -LSP mode supports the following Features: + (defvar my/detect-prog-mode/special-names + '((c . c-mode) + (cpp . c++-mode) + (emacslisp . emacs-lisp-mode) + (html . web-mode) ;; I intentionally want to use this alternative. + (matlab . octave-mode) + (shell . shell-script-mode) + (visualbasic . visual-basic-mode) + (xml . sgml-mode)) + "Names in this alist map a language to its mode; all other languages 𝒳 have mode ‘𝒳-mode’ afaik.") -+ As you type reporting of parsing and compilation errors (via flycheck/lsp-ui) -+ Code completion - using company-mode -+ doc hovers - using lsp-ui -+ Code actions - using lsp-ui -+ Code outline - using builtin [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Imenu.html][imenu]] -+ Code navigation - using builtin xref -+ Code lens (references/implementations) - using builtin [[https://www.gnu.org/software/emacs/manual/html_node/emacs/Xref.html][xref]] -+ Highlights -+ Code formatting -+ Visual debugger - [[https://github.com/emacs-lsp/dap-mode][dap-mode]] -+ Test runner - dap-mode -+ Project explorer integration - treemacs + (let* ((lang (if string (language-detection-string string) (language-detection-buffer))) + (mode (or (cdr (assoc lang my/detect-prog-mode/special-names)) + (intern (format "%s-mode" lang))))) + (if (called-interactively-p 'any) + (progn (call-interactively mode) (message "%s enabled!" mode)) + mode))) +#+end_src ++ ⌘-e to edit an Org table cell; see [[https://orgmode.org/manual/Built_002din-Table-Editor.html][here]] for built-in table editing commands. #+begin_src emacs-lisp -;; Load the various useful utils -(require 'lsp-ui-peek) -(require 'lsp-ui-sideline) -(require 'lsp-ui-doc) -(require 'lsp-ui-imenu) +(advice-add #'org-edit-special :before-until + (lambda (&rest r) + (when (equal 'table-row (car (org-element-at-point))) + (call-interactively #'org-table-edit-field)))) +#+end_src +** COMMENT Pair Programming +:PROPERTIES: +:CUSTOM_ID: Pair-Programming +:END: -; (setq lsp-mode-hook nil) -(add-hook 'lsp-mode-hook - (lambda () - ;; Locally delete a file needed for work, but it's outdated and clashes with LSP. - ;; TODO: (shell-command "rm ~/wxPortal/.flowconfig") - ;; Load the various useful utils - (require 'lsp-ui) - (lsp-ui-peek-enable t) - (lsp-ui-doc-enable t) - (lsp-ui-sideline-enable t) - (lsp-ui-imenu-buffer--enable) - ;; Set ⌘-l as the main mini-menu for LSP commands - (bind-key* "s-l" #'my/lsp-hydra/body))) +# Add: Quickly toggle my Emacs for when doing pair programming -(defun my/helm-lsp-workspace-symbol-at-point () - (interactive) - (let ((current-prefix-arg t)) - (call-interactively #'helm-lsp-workspace-symbol))) +I try to make my Emacs look more predictable for my colleagues, by introducing a bunch of UI elements that I normally +have off by default but would otherwise serve as useful aids when working together or provide a sense of familiarity for +my VSCoder counterparts. - (defun my/helm-lsp-global-workspace-symbol-at-point () - (interactive) - (let ((current-prefix-arg t)) - (call-interactively #'helm-lsp-global-workspace-symbol))) +#+begin_src emacs-lisp :tangle ~/Desktop/work.el +(defun my/toggle-pair-programming () + "Toggle enabling features that might aid communication when sharing screen, or pair programming -;; TODO: Add other cool features discussed/loaded above into this hydra! -(defhydra my/lsp-hydra (:color blue :hint nil) - ;; Xref - ("d" xref-find-definitions "Definitions" :column "Xref") - ("D" xref-find-definitions-other-window "-> other win") - ("r" xref-find-references "References") - ("s" my/helm-lsp-workspace-symbol-at-point "Helm search") - ("S" my/helm-lsp-global-workspace-symbol-at-point "Helm global search") +- Full screen mode +- Line numbers, and column numbers, and cat progress bar +- Blamer overlays +- Dark theme +- Tabs +- Folder navigator +" + (interactive) + (defvar my/sharing-screen? +1) + (setq my/sharing-screen? (- my/sharing-screen?)) ;; Toggle between +1 and -1. - ;; Peek - ("C-d" lsp-ui-peek-find-definitions "Definitions" :column "Peek") - ("C-r" lsp-ui-peek-find-references "References") - ("C-i" lsp-ui-peek-find-implementation "Implementation") + ;; When pair programming, exit full screen mode so others can “draw” on my screen, eg using Slack + (toggle-frame-fullscreen) - ;; LSP - ("p" lsp-describe-thing-at-point "Describe at point" :column "LSP") - ("C-a" lsp-execute-code-action "Execute code action") - ("R" lsp-rename "Rename") - ("t" lsp-goto-type-definition "Type definition") - ("i" lsp-goto-implementation "Implementation") - ("f" helm-imenu "Filter funcs/classes (Helm)") - ("C-c" lsp-describe-session "Describe session") + ;; Line numbers in the left margin + (setq display-line-numbers-width-start t) + (global-display-line-numbers-mode my/sharing-screen?) - ;; Flycheck ---my “C-c !” flycheck hydra is much better than this simple lsp one. - ;; ("l" lsp-ui-flycheck-list "List errs/warns/notes" :column "Flycheck") - ("l" my/flycheck-hydra/body "List errs/warns/notes" :column "Flycheck") + ;; Column & line numbers in the modeline; always want these on? + (column-number-mode +1) ;; Enabled in doom-modeline by default? + (line-number-mode +1) ;; Enabled in doom-modeline by default? - ;; Misc - ("q" nil "Cancel" :column "Misc") - ("b" pop-tag-mark "Back")) -#+end_src + ;; Progress bar, graphic and percentage + (use-package nyan-mode ) + (nyan-mode my/sharing-screen?) -#+begin_details End-to-end Test -#+begin_src emacs-lisp :tangle init-test.el -(ert-deftest lsp-hover-shows-type-signature () - ;; Make a temporary scratch.js file with the given contents. - (-let [scratch.js (make-temp-file "scratch" nil ".js" "const first = (x, y) => 3")] - (find-file scratch.js) - (lsp-workspace-folders-add (f-parent scratch.js)) - (lsp) + ;; Who did what and when? + (global-blamer-mode my/sharing-screen?) - ;; lsp-hover uses lsp--eldoc-message, so let's save the hover info. - (advice-add #'lsp--eldoc-message :before (lambda (&rest msg) (setq my/lsp-hover-message (substring-no-properties (car msg))))) + ;; Make things look like VSCode; reduce friction with others + ;; → Dark theme, tabs, and clickable folder navigation + (if (= +1 my/sharing-screen?) + (my/toggle-theme 'doom-dracula) + (my/toggle-theme 'doom-solarized-light)) - (end-of-buffer) - (insert "\n first") - (lsp-hover) ;; Alternatively: (lsp-describe-thing-at-point) - (should (equal "const first: (x: any, y: any) => number" my/lsp-hover-message)) + (use-package centaur-tabs) + (setq centaur-tabs-set-icons t) + (setq centaur-tabs-gray-out-icons 'buffer) + (setq centaur-tabs-set-bar 'over) + (centaur-tabs-mode my/sharing-screen?) - (save-buffer) - (kill-buffer))) + ;; Treemacs is feature-right; e.g. highlights file associated to current buffer automatically and fully mouse-complaint. + ;; press ? to summon a helpful hydra + (use-package treemacs) + (if (= +1 my/sharing-screen?) ;; Toggle showing treemacs window + (treemacs) + (ignore-errors (delete-window (treemacs-get-local-window)))) + ;; Press “C-e” to see the end of length file names, or use the mouse to widen treemacs + (treemacs-toggle-fixed-width) + (ignore-errors (treemacs-indent-guide-mode +1)) + (treemacs-resize-icons 18) + (other-window -1) + + ;; Kickoff message + (if (= +1 my/sharing-screen?) + (message "Pair programming provides an excellent test of technical and social skills"))) + +(my/toggle-pair-programming) #+end_src -#+end_details -*** Types as comments in JavaScript via LSP && Using LSP for Org-Src Blocks +** Eldoc for Lisp and Haskell ---documentation in the mini-buffer :PROPERTIES: -:CUSTOM_ID: Types-as-comments-in-JavaScript-via-LSP +:CUSTOM_ID: Eldoc-for-Lisp-and-Haskell :END: -#+begin_src js :tangle "~/.emacs.d/scratch.js" -// The following enables typechecking via LSP. -// @ts-check -/** - ,* - ,* @param {number} x - - ,* @param {number} y - - ,* @returns {number} - - ,*/ -// function add(x, y) {return "x + y";} // ⚠ Faulty implementation! -function add(x, y) {return x + y;} +In =emacs-lisp-mode= we can enable =eldoc-mode= ---“Elisp Live Documentation”--- to +display information about a function or a variable in the echo area. Likewise +for Haskell. -// add("a", 1) // ⚠ Erroneous call! -add(1, 2) -#+end_src +#+BEGIN_SRC emacs-lisp +(use-package eldoc + :hook (emacs-lisp-mode . turn-on-eldoc-mode) + (lisp-interaction-mode . turn-on-eldoc-mode) + (haskell-mode . turn-on-haskell-doc-mode) + (haskell-mode . turn-on-haskell-indent)) -For more on this idea on types, see [[https://github.com/BeyondCodeBootcamp/jsdoc-typescript-starter][A simple, but non-trivial example of getting the most from JSDoc + tsserver (Type Linting without TypeScript)]]. +;; Slightly shorten eldoc display delay. +(setq eldoc-idle-delay 0.4) ;; Default 0.5 +#+END_SRC -Within Org-mode, at the start of the above ~src~ block, execute ~M-x lsp-org~ to [[https://emacs-lsp.github.io/lsp-mode/manual-language-docs/lsp-org/][get -some LSP]] working even though you're in Org-mode. +The less casual Haskeller would likely want to use [[https://haskell-lang.org/intero][intero]] to obtain +more support; e.g., obtain suggestions from GHC about redundant imports +or type signatures. -** JSON +** COMMENT Modern Browsing within Emacs :Disabled: :PROPERTIES: -:CUSTOM_ID: JSON +:CUSTOM_ID: Modern-Browsing-within-Emacs :END: -Since web apps tend to be RESTful, payloads are [[https://www.json.org/json-en.html][JSON]]. To use the JSON, we need -to tediously find paths to particular fields; let's do so with /automatically, -without error./. - -When we open a JSON file, we are promoted to install an LSP server, which -- Checks that the file is valid JSON. -- Shows the full path to the cursor's location, at the top of the window. +#+begin_src shell :tangle no +# https://github.com/d12frosted/homebrew-emacs-plus +$ brew tap d12frosted/emacs-plus +$ brew install emacs-plus@29 --with-xwidgets +$ /usr/local/Cellar/emacs-plus@29/29.0.50/bin/emacs-29.0.50 & -#+begin_src emacs-lisp -(use-package json-mode :defer nil) +# In ~/.bashrc, put the following at the end: +alias emacs="/usr/local/Cellar/emacs-plus@29/29.0.50/bin/emacs-29.0.50" #+end_src -Useful bindings are in the docstring of TODO. doc.json-mode. -+ kbd:C-c_C-f :: Pretty print buffer -+ kbd:C-c_C-p :: Copy path to field at point +-------------------------------------------------------------------------------- -Note that there is also TODO. doc.json-pretty-print-buffer; which can be used to -uglify a JSON buffer if a prefix is provided. +I like using Chrome ---I like the integration of all things Google. -Let's make a hydra for JSON, #+begin_src emacs-lisp -(my/defhydra nil "JSON Browser" gamepad - :Buffer - ("p" #'json-mode-show-path "Copy path to field at point") - ;; ("f" #'json-mode-beautify "Format Buffer") - ;; ("m" (lambda () (interactive) (json-pretty-print-buffer t)) "Minify/ugligy buffer") - ("t" (lambda () (interactive) - (if my/json-hydra/pretty-printed? - (json-pretty-print-buffer t) - (json-mode-beautify (point-min) (point-max))) - (setq my/json-hydra/pretty-printed? (not my/json-hydra/pretty-printed?))) - "Toggle format/uglify of buffer" - :toggle (progn (defvar my/json-hydra/pretty-printed? nil) - my/json-hydra/pretty-printed?))) +(cl-defun internet (&optional (url (concat "https://www." (read-string "https://www.")))) + "Browse to URL using `xwidget-webkit-browse-url'; see also `browse-url'." + (interactive) + (delete-other-windows) + (split-window-right) + (xwidget-webkit-browse-url url)) -;; TODO: (bind-key "C-c SPC" 'my/hydra/JSON\ Browser/body 'json-mode-map) -;; NOTE: “C-x SPC” is for rectangle editing. +(my/defhydra "C-c p" "Emacs Browser" gamepad + :Internet + ("m" (internet "https://mail.google.com/mail/u/0/#inbox") "gMail" :exit t) + ("c" (internet "https://calendar.google.com/calendar/u/0/r") "gCalendar" :exit t) + ("e" (internet "https://www.reddit.com/r/emacs/") "Emacs Forum" :exit t) + ("b" (internet) "Browse" :exit t)) #+end_src -Interesting, but not for me: [[https://github.com/taku0/json-par/][json-par: Emacs minor mode for structural editing -of JSON]]. - -Also: [[https://transform.tools/json-to-jsdoc][JSON to X]] is a website to convert JSON to other formats, such as in Rust -or as JSDoc type annotations. -** w-screencapture +** An “auto read only” detection mechanism ---when jumping to definitions :Disabled: :PROPERTIES: -:CUSTOM_ID: w-screencapture +:CUSTOM_ID: An-auto-read-only-detection-mechanism-when-jumping-to-definitions +:header-args: :tangle no :END: -#+begin_src emacs-lisp -(bind-key "C-c s" - (cl-defun w-screencapture () - "Interactively capture screen and save to clipboard; then paste in Slack, etc, with ⌘-c. - - After we run this command, we can swipe up on mousepad to select different desktops, then - click & drag to select portition of screen to capture. - - Captured screen is NOT saved to disk, only copied to clipboard. - -In MacOs, -+ Command + Shift + 5 ⇒ Select screen record -+ Command + Shift + 4 ⇒ Selection Screenshot -+ Command + Shift + 3 ⇒ Screenshot -See: https://osxdaily.com/2011/08/11/take-screen-shots-terminal-mac-os-x" - (interactive) - (async-shell-command "screencapture -i -c"))) +Files in 3ʳᵈ-party directories should be read-only whenever I open them, when +I'm jumping to definitions (with ~M-.~). -(cl-defun w-delete-all-screenshots () - "Delete all “Screen Shot ⋯” files in ~/Desktop." - (interactive) - (thread-last (shell-command-to-string "cd ~/Desktop; ls") - (s-split "\n") - (--filter (s-starts-with-p "Screen Shot" it)) - (--map (f-delete (format "~/Desktop/%s" it))))) +#+begin_src emacs-lisp +;; Usage: Press “M-”. “use-package” below and you can accidentally alter the source code! +;; But in this case you likely just wanted to see the 3ʳᵈ-party definition, not alter it. +;; As such, with this advice, the source will not be alterable (unless you toggle read-only mode). +(advice-add #'xref-find-definitions :after + (lambda (&rest _) + (when (--map (s-ends-with? it (f-parent buffer-file-name)) + '("lisp/emacs-lisp" "/lisp" ".emacs.d/elpa/")) + (read-only-mode)))) #+end_src -** Screencapturing the Current Emacs Frame -:PROPERTIES: -:CUSTOM_ID: Screencapturing-the-Current-Emacs-Frame -:END: - -Sometimes an image can be tremendously convincing, or at least sufficiently -inviting. The following incantation is written for MacOS and uses it's native -=screencapture= utility, as well as =magick=. -#+BEGIN_SRC emacs-lisp -(defun my/capture-emacs-frame (&optional prefix output) -"Insert a link to a screenshot of the current Emacs frame. - -Unless the name of the OUTPUT file is provided, read it from the -user. If PREFIX is provided, let the user select a portion of the screen." -(interactive "p") -(defvar my/emacs-window-id - (s-collapse-whitespace (shell-command-to-string "osascript -e 'tell app \"Emacs\" to id of window 1'")) - "The window ID of the current Emacs frame. - - Takes a second to compute, whence a defvar.") - -(let* ((screen (if prefix "-i" (concat "-l" my/emacs-window-id))) - (temp (format "emacs_temp_%s.png" (random))) - (default (format-time-string "emacs-%m-%d-%Y-%H:%M:%S.png"))) -;; Get output file name - (unless output - (setq output (read-string (format "Emacs screenshot filename (%s): " default))) - (when (s-blank-p output) (setq output default))) -;; Clear minibuffer before capturing screen or prompt user -(message (if prefix "Please select region for capture …" "♥‿♥")) -;; Capture current screen and resize -(thread-first - (format "screencapture -T 2 %s %s" screen temp) - (concat "; magick convert -resize 60% " temp " " output) - (shell-command)) -(f-delete temp) -;; Insert a link to the image and reload inline images. -(insert (concat "[[file:" output "]]"))) -(org-display-inline-images nil t)) -(bind-key* "C-c M-s" #'my/capture-emacs-frame) -#+END_SRC +* Open PDFs in Emacs +#+begin_src emacs-lisp + ;; In Org-mode, clicking on PDF should open it in Emacs + ;; Example: [[~/Desktop/stuff-I'm-learning.pdf::12]] ;; Opens the pdf to page 12 + ;; Another Example: docview:~/Desktop/stuff-I'm-learning.pdf::12 + (ignore-errors (add-to-list 'org-file-apps '("\\.pdf\\'" . emacs))) + + ;; Required code to make the above links work as expected. + ;; Source: https://www.reddit.com/r/emacs/comments/re0dx8/how_do_you_link_a_specific_pdf_page_in_org_mode/ + (defun my-org-docview-open-hack (orig-func &rest args) + (let* ((link (car args)) path page) + (string-match "\\(.*?\\)\\(?:::\\([0-9]+\\)\\)?$" link) + (setq path (match-string 1 link)) + (setq page (and (match-beginning 2) + (string-to-number (match-string 2 link)))) + (org-open-file path 1) + (when page + (cond + ((eq major-mode 'pdf-view-mode) + (pdf-view-goto-page page)) + (t + (doc-view-goto-page page)))))) + (advice-add 'org-docview-open :around #'my-org-docview-open-hack) + + ;; Alternatively, there's a dedicated package for this + ;; https://github.com/fuxialexander/org-pdftools/tree/967f48fb5038bba32915ee9da8dc4e8b10ba3376 +#+end_src + +* empv +#+begin_src emacs-lisp +(use-package empv ) + +;; Then, M-x empv-play https://invidious.fdn.fr/watch?v=hlTqCmpP5eo, to listen to Dua Ifitiah in the background +;; Or: M-x empv-play https://invidious.fdn.fr/watch?v=9m9yE7qtq5w +;; lol maybe make a hydra(ie playlist) of things I commonly listen to in the background while working. +;; Require: brew install mpv +;; See also: +;; Maybe better? https://github.com/spiderbit/ytdious/tree/941460b51e43ef6764e15e2b9c4af54c3e56115f +;; Maybe better? https://melpa.org/#/yeetube +;; https://github.com/maximus12793/helm-youtube/tree/e7272f1648c7fa836ea5ac1a61770b4931ab4709 +;; https://github.com/isamert/empv.el/tree/1721a581d68f211a7f0104554858ea2afb1723ff +;; +(setq empv-invidious-instance "https://invidious.fdn.fr.com") +#+end_src -Why this way? On MacOS, ImageMagick's =import= doesn't seem to work ---not at all -for me! Also, I dislike how large the resulting image is. As such, I'm using -MacOS's =screencapture= utility, which in-turn requires me to somehow obtain frame -IDs. Hence, the amount of work needed to make this happen on my system was most -simple if I just wrote it out myself rather than tweaking an existing system. +* TODO COMMENT Eglot ----Fancy IDE Features that Just Workᵀᴹ -+ ~C-c C-x C-v~ ⇒ Toggle inline images! +For JavaScript: +#+begin_src emacs-lisp +;; Get the language server for JavaScript +;; $ npm install -g javascript-typescript-langserver -** Comment-boxes up to the fill-column ---or banner instead? +;; Now open an empty buffer, switch to JS mode, M-x eglot, M-x company-mode, +;; then enter “console.” and see the completion of possibilities! <3 +#+end_src + +For Java, install doc:eglot-java-mode and activate that in a Java file ---it automatically installs the required +language server. 🚀 + +* COMMENT Lost Souls :Outdated_Documentation:Not_yet_tangled: :PROPERTIES: -:CUSTOM_ID: Comment-boxes-up-to-the-fill-column +:CUSTOM_ID: Lost-Souls :END: +** Note: M-S-SPC is for my personal servers dashboard. +:PROPERTIES: +:CUSTOM_ID: Note-M-S-SPC-is-for-my-personal-servers-dashboard +:END: +#+begin_src emacs-lisp :tangle no +;; Note: M-S-SPC is for my personal servers dashboard. +(global-set-key (kbd "M-SPC") (lambda () (interactive) (setq org-agenda-files (list org-default-notes-file)) (org-agenda nil "a") (delete-other-windows) (beginning-of-buffer))) -GIF: https://endlessparentheses.com/images/comment-box.gif - -#+begin_src emacs-lisp -(defun my/comment-box (b e) - "Draw a box comment around the region but arrange for the region -to extend to at least the fill column. Place the point after the -comment box. +(use-package ace-jump-mode ;; Already installed above, somewhere. + :config (bind-key* "C-c SPC" 'ace-jump-mode)) +#+end_src -Source: http://irreal.org/blog/?p=374 +** Zoom +:PROPERTIES: +:CUSTOM_ID: Zoom +:END: +#+begin_src emacs-lisp :tangle no +;; An automatic window-resizing mechanism. +;; A “calmer” alternative to golden-ratio. +;; https://github.com/cyrus-and/zoom +(use-package zoom -To do fancy stuff like removing boxes, centering them, etc -see https://github.com/lewang/rebox2/blob/master/rebox2.el" - (interactive "r") - (let ((e (copy-marker e t))) - (goto-char b) - (end-of-line) - (insert-char ? (- fill-column (current-column))) - (comment-box b e 1) - (goto-char e) - (set-marker e nil))) + :config (zoom-mode t)) #+end_src +** Ibuffer +:PROPERTIES: +:CUSTOM_ID: Ibuffer +:END: +#+begin_src emacs-lisp :tangle no +;; Let's use an improved buffer list. +(use-package ibuffer ;; This is built-into Emacs. + :bind ("C-x C-b" . ibuffer)) +;; It uses similar commands as does dired; e.g., +;; / . org +;; This filters (“/”) the list with extensions (“.”) being “org”. -A comment box sometimes increases the size of a file more than I'd like, or more than others on my dev team would like. -As such, let's try [[https://github.com/WJCFerguson/banner-comment][banner comments]]: -- GIF: https://github.com/WJCFerguson/banner-comment/blob/35d3315683d3f97605207691b77e9f447af18fe2/demo.png -#+begin_src emacs-lisp -(use-package banner-comment :defer nil) -#+end_src +(use-package ibuffer-vc + :hook (ibuffer . (lambda () + (ibuffer-vc-set-filter-groups-by-vc-root) + (unless (eq ibuffer-sorting-mode 'alphabetic) + (ibuffer-do-sort-by-alphabetic)))) + :custom + (ibuffer-formats '((mark modified read-only " " + (name 18 18 :left :elide) " " + (size 9 -1 :right) " " + (mode 16 16 :left :elide) " " + (vc-status 16 16 :left) " " + (vc-relative-file))))) + #+end_src ++ [[http://martinowen.net/blog/2010/02/03/tips-for-emacs-ibuffer.html][Tips for using Emacs Ibuffer]] ++ (~10 minute video) [[https://cestlaz.github.io/posts/using-emacs-34-ibuffer-emmet/][Using Emacs - 34 - ibuffer and emmet - C'est la Z]] ++ (~10 minute video) [[https://www.youtube.com/watch?v=6KN_oSLFf-k&ab_channel=ProtesilaosStavrou][Emacs: introduction to IBUFFER - YouTube]] -Pretty slick! +** find function at point +:PROPERTIES: +:CUSTOM_ID: find-function-at-point +:END: +Friendly reminder that ~M-.~ takes you to the definition, and ~M-,~ takes you back +to where you originally where. +** COMMENT centered-cursor: Keep focus in the centre of the screen :Eeek: +:PROPERTIES: +:CUSTOM_ID: COMMENT-centered-cursor-Keep-focus-in-the-centre-of-the-screen +:END: +#+begin_src emacs-lisp :tangle no + ;; When scrolling, move the screen and leave the cursour in the (vertical) center of the screen + (use-package centered-cursor-mode + :config (global-centered-cursor-mode)) +#+end_src -** TODO Auto-format on Save :Reenabled:Should_be_Disabled:Breaks_HTML_export_of_org_files_with_unicode_or_emojis: +** “C-x 2” and “C-x 3” now create a new window horizontally/vertically and send cursor there :PROPERTIES: -:CUSTOM_ID: Auto-format-on-Save +:CUSTOM_ID: C-x-2-and-C-x-3-now-create-a-new-window-horizontally-vertically-and-send-cursor-there :END: + #+begin_src emacs-lisp :tangle no +;; When we split open a new window, we usually want to jump to the new window. +(advice-add #'split-window-below :after (lambda (&rest _) (other-window 1))) +(advice-add #'split-window-right :after (lambda (&rest _) (other-window 1))) + #+end_src -[[https://github.com/lassik/emacs-format-all-the-code/tree/eb2a7fa6da15d23b57921218a36ac67d523e81f1][Auto-format source code in many languages with one command]] +** Semantic Change +:PROPERTIES: +:CUSTOM_ID: Semantic-Change +:END: -Let's auto-format source code in many languages using the same command for all -languages, instead of learning a different Emacs package and formatting command -for each language. +Using kbd:⌘-i and kbd:⌘-o we can quickly, for example, delete a string or its +contents; or delete a {}-block or just its contents; or delete a ()-argument +list or just its contents, etc. -Just do ~M-x format-all-buffer~ and it will try its best to do the right thing. To -auto-format code on save, use the minor mode format-all-mode. Better yet, we ask -for it to do so “on save”. + ~change-inner~ gives you vim's ~ci~ command: +- ~change-inner {~ ⇒ Delete all text starting from the first ‘{’ delimiter to the next one; but /keep the delimiters/. +- ~change-outer {~ ⇒ As above, but also delete the delimiters. -- You will need to install external programs to do the formatting. If - ~format-all-buffer~ can't find the right program, it will try to tell you how to - install it. -- An alternative Emacs tool is [[https://github.com/radian-software/apheleia][apheleia: 🌷 Run code formatter on buffer - contents without moving point, using RCS patches and dynamic programming.]] +#+begin_src emacs-lisp :tangle no +(use-package change-inner -#+begin_src emacs-lisp -(use-package format-all - :defer nil - ;; To enable format on save for most programming language buffers: - :hook (prog-mode . format-all-mode) - :config - ;; Please use the default formatters; I don't care too much. - (add-hook 'format-all-mode-hook 'format-all-ensure-formatter)) + :bind (("s-i" . #'change-inner) + ("s-o" . #'change-outer))) #+end_src -[[https://prettier.io/docs/en/index.html][Prettier]] is an opinionated code formatter for numerous web related languages, -including JS, TS, HTML, CSS, JSON, Vue, Markdown, and YAML. +** Drag Stuff :Disabled: +:PROPERTIES: +:CUSTOM_ID: Drag-Stuff +:END: +#+begin_src emacs-lisp :tangle no :tangle no +;; Move current word ←/→, or current line ↑/↓. +;; Todo: Compare with org-metaup and org-metadown... +(use-package drag-stuff -#+begin_src emacs-lisp -;; For JavaScript prettification: It automatically inserts semicolons, forces newlines, inserts parens, etc. -;; Lots of redundant stuff, but stuff to make it easy to work with others. -(shell-command "npm install --global prettier") -;; Specific package to do only JS prettification: https://github.com/prettier/prettier-emacs + :config (cl-loop for (key . action) in '(("" . drag-stuff-down) + ("" . drag-stuff-up) + ("" . drag-stuff-right) + ("" . drag-stuff-left)) + do (bind-key key action org-mode-map)) + (drag-stuff-global-mode 1)) #+end_src -For example, in a NodeJS app make a file ~.prettierrc.json~ whose contents are -#+begin_src json :tangle no -{ - "singleQuote": true, - "arrowParens": "avoid", - "printWidth": 120, - "semi": false, - "trailingComma": "none" -} -#+end_src -Unfortunately, my current team prefers ~"semi": true~ ---which is understandable -for people not familar with how JavaScript does [[https://www.freecodecamp.org/news/lets-talk-about-semicolons-in-javascript-f1fe08ab4e53/][semicolon insertion]]. +Ruins Org-mode's M-↑/↓ for moving entire sections around. -( If you're having trouble getting prettier to work on save, consider using this -[[https://github.com/codesuki/add-node-modules-path][package]] which detects your node modules' path. ) +** Indentation Guide +:PROPERTIES: +:CUSTOM_ID: Indentation-Guide +:END: -Anyhow, with these rules: -- I write =const f = (x) => "x" == x= then on save it becomes =const f = x => 'x' == x;= -- I write =let xs = [1, 2, 3,]= then on save it becomes =let xs = [1, 2, 3];= -- I write =foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());= - then nothing happens on save; but if you add one more argument, =, 12=, then it gets reformatted with each arg on one line. +The following is also “OK” in Org-mode ;-) +#+begin_src emacs-lisp :tangle no +;; Add a visual indent guide +(use-package highlight-indent-guides + :hook (prog-mode . highlight-indent-guides-mode) + :custom + (highlight-indent-guides-method 'character) + (highlight-indent-guides-character ?|) + (highlight-indent-guides-responsive 'stack)) +#+end_src -Notice that in the first example above, only minor syntactic changes were made; -nothing altering semantics or quality of code ---for that, we use a /linter/. -In particular, [[https://emacs-lsp.github.io/lsp-mode/page/lsp-eslint/][LSP ESLint]] when activated will show us two errors: It's almost always -better to use ~===~ instead of ~==~ and literals should be on the right side of the equality. -We can use ~M-x lsp-execute-code-action~ to get a list of actions that can be performed to fix this /quality/ problem. +** TODO COMMENT [Nope?] JavaScript +:PROPERTIES: +:CUSTOM_ID: COMMENT-Nope-JavaScript +:END: + ++ ;; FIXME Error (use-package): Failed to install ob-js: Package ‘ob-js-’ is unavailable -** COMMENT Searching Hydra +#+begin_src emacs-lisp :tangle no +(use-package ob-js + :config + (add-to-list 'org-babel-load-languages '(js . t)) + (org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages) + (add-to-list 'org-babel-tangle-lang-exts '("js" . "js")) + (system-packages-ensure "node")) + ;; use “:results output” for js blocks! + #+end_src +** Commenting :PROPERTIES: -:CUSTOM_ID: Searching-Hydra +:CUSTOM_ID: Commenting :END: -#+begin_src emacs-lisp -(my/defhydra "s-f" "\t\tLocate Everything" search - :Buffer - ;; find all the occurrences of a string, pull out the lines containing the string to another buffer where [F2] I can edit and save, - ("e" helm-swoop "Editable") - ;; Implicit Regex, colourful - ("c" swiper "Classic") - :Project - ;; “:toggle ℰ”: ℰ is a Boolean expression that is evaluated to tell us whether the state is on-or-off - ("t" (lambda () (interactive)) "Ignore specs/jsons" - :toggle (let* ((with-hole "ag %s --line-numbers -S --color --nogroup %%s %%s %%s") ;; ≈ original value of ‘helm-grep-ag-command’ - (ignores "--ignore=\"*spec.js\" --ignore=\"*.json\" --ignore=\"*.json5\"") - (on (equal helm-grep-ag-command (format with-hole ignores)))) - (if on (progn (setq helm-grep-ag-command (format with-hole "")) nil) ;; ≈ turn off the toggle - (setq helm-grep-ag-command (format with-hole ignores))))) - ("f" (lambda () (interactive) (helm-do-grep-ag t)) "File type") - ("d" (lambda () (interactive) (-let [default-directory (read-directory-name "Where do you want to search? ")] (helm-do-grep-ag nil))) "Directory") - ("D" (lambda () (interactive) (-let [default-directory (read-directory-name "Where do you want to search? ")] (helm-do-grep-ag t))) "Directory & type")) -#+end_src -** COMMENT Peer Review / Pull Request Template for Work +Let's get some nifty [[https://github.com/remyferre/comment-dwim-2][commenting]] features ---the link has nice usage gifs. ++ [[kbd:][M-;]] + repeatedly does (1) comments current line, (2) inserts a comment at the end + of the current line, and (3) deletes an existing end-of-line comment. + + [[kbd:][C-u_M-;]] indents the current enf-of-line comment with any above it. + + For use with Org-mode, it's best to use doc:org-edit-src-code ---which I've + bound to [[kbd:][⌘ e]]. + #+begin_src emacs-lisp :tangle no +(use-package comment-dwim-2 + :bind ("M-;" . comment-dwim-2)) + + ;; Not ideal: M-; comments a parent Org heading and not the current line. + ;; (define-key org-mode-map (kbd "M-;") 'org-comment-dwim-2) + #+end_src + +** Having a workspace manager in Emacs :PROPERTIES: -:CUSTOM_ID: Peer-Review-Pull-Request-Template-for-Work +:CUSTOM_ID: Having-a-workspace-manager-in-Emacs :END: -I run TODO. doc.w-pr-template to get a new Org buffer with a review template. I make -notes and check-off boxes with ~C-c C-c~ as usual, and when I'm done I press ~C-c -C-s~ to copy the buffer to clipboard, in markdown format. Then I can paste it -into Github. +I've loved using XMonad as a window tiling manager. I've enjoyed the ability to +segregate my tasks according to what ‘project’ I'm working on; such as research, +marking, Emacs play, etc. With [[https://github.com/nex3/perspective-el][perspective]], I can do the same thing :-) -#+begin_src emacs-lisp -(cl-defun w-pr-template () - "Hi" - (interactive) - (-let [buf "PR Template ~ Press “C-c C-s” when done"] - (ignore-errors (kill-buffer buf)) - (switch-to-buffer buf) - (insert "w-pr-template") - (yankpad-expand) - (org-mode) - (beginning-of-buffer) - (use-local-map (copy-keymap org-mode-map)) - (local-set-key (kbd "C-c C-s") - `(lambda () - (interactive) - (beginning-of-buffer) - (replace-string "[X]" "✅") - (beginning-of-buffer) - (replace-string "[ ]" "❌") - (beginning-of-buffer) - (replace-string "[-]" "🚧") - (-let [org-export-with-toc nil] - (org-md-export-as-markdown) - (kill-ring-save (point-min) (point-max))) - (kill-buffer-and-window) ;; Kills the new org-md-export buffer - (kill-buffer ,buf) ;; Kills this temporary PR template buffer - (message "PR notes saved to clipboard in Github markdown"))))) -#+end_src +That is, I can have a million buffers, but only those that belong to a workspace +will be visible when I'm switching between buffers, for example. +( The awesome-tab and centaur-tab, mentioned elsewhere here, can be used to +achieve the same thing by ‘grouping buffers together’. ) -#+begin_src org :tangle "~/.emacs.d/yankpad.org" :comments none -,** w-pr-template: Peer Review / Pull Request Template for Work -I followed the testing instructions and everything look's good 😁 +#+begin_src emacs-lisp :tangle no +(use-package perspective -Below is a detailed checklist of what I went through. + :config ;; Activate it. + (persp-mode) + ;; In the modeline, tell me which workspace I'm in. + (persp-turn-on-modestring)) +#+END_SRC --------------------------------------------------------------------------------- +All commands are prefixed by ~C-x x~; main commands: ++ ~s, n/→, p/←~ :: ‘S’elect a workspace to go to or create it, or go to ‘n’ext + one, or go to ‘p’revious one. ++ ~c~ :: Query a perspective to kill. ++ ~r~ :: Rename a perspective. ++ ~A~ :: Add buffer to current perspective & remove it from all others. -1. [ ] Pull Request: The PR template was fully filled out. - - [ ] Clear description of the problem and how it was solved. - - [ ] I've cross-checked the description with the associated Jira ticket; and - everything is implemented. - - [ ] I've ticked-off the PR's check-boxes. - - [ ] Good use of bullet points (-) and code font (‵, ‷) to make the prose - easier to read. - - [ ] The commit messages are well-written. - - [ ] Travis CI succeeds. - - [ ] PR author annotated source code, with Github comments, before the review. - - Annotations guide the reviewer through the changes, showing which files to look at first and defending the reason behind each code modification. +As always, since we've installed ~which-key~, it suffices to press ~C-x x~ then look +at the resulting menu 😃 -2. [ ] Functionality: The code behaves as the author intended. - - [ ] I was able to reproduce the bug on ~main~. - - [ ] Ran the code and used it as an end-user would. - Namely, I made a new form, submitted an instance, checked ~In-Progress~, - & ~Form Reports~. - - [ ] I've tried all kinds of quotes and unicode, "𝒰"\‘ℕ’/𝒊′ℭ″𝑂؛𝒟⨾'∃', - input for text inputs; as well as special chars: +-_)(*&^%$#@!~`'".;> - - Remove some redundancy using a bit of laws of algorithmics, namely - + ~[𝒆𝒕𝒂-𝒓𝒆𝒅𝒖𝒄𝒕𝒊𝒐𝒏] ⋯(x => f(x))⋯ ≡ ⋯f⋯~ - + ~[𝒊𝒇-𝒅𝒊𝒔𝒕𝒓𝒊𝒃𝒖𝒕𝒊𝒗𝒊𝒕𝒚] (a ? f(b) : f(c)) ≡ f(a ? b : c)~ - which increase readability a tad. - # Especially when “f” is a lengthy expression; it may also be ideal to give - # “a ? b : c” a local name. - - Fail fast, validate arguments [we have some in-house validation util libraries] - - Be aware that ~0, "", []~ are all falsey values in JS: If a variable ~x~ - can be one of those things, then ~if (x)~ is not always approriate; better - may be ~if(typeof x === 'integer')~ since this communicates two things (1) - the variable is defined, and (2) what it's type expected is. - + Likewise, better use ~typeof~ instead of ~x !== null~. - - You have variables declared a bit from their use sites; the distance - creates an unnecessary disconnect ---especially since you don't use these - variables elsewhere. Please relocate them to be closer to their use sites. - - Strings are sanitised - - Errors are caught; with ~try/catch~ - - Global variables are avoided, when possible. - - ~const~ is preferred to ~let~; ~var~ should seldom be used. - - Use ~var~ and ~function~ when you want definitions hoisted to the top of - their enclosing scope. - - ~===~ is preferred to ~==~. - - Use default arguments instead of short-circuiting or conditionals - - ~f (x) { x = x || defaultValue; ⋯ }~ ≡ ~f (x = defaultValue) { ⋯ }~. - - Named parameters can also be optional, with default values: - ~f(obj) { let prop = obj.prop || defaultProp; ⋯}~ ≣ ~f ({prop = - defaultProp}) { ⋯ }~ - - Unless you really need an array, handled an indefinite number of - arguments using rest parameters: ~function f(...args) {⋯ // use ‘args’ as - an array}~ can be invoked ~f(x₁, x₂, …)~ _without_ array brackets; or as - ~f(...arr)~ if you have an array in-hand. - - Function arguments: 3 or fewer ideally - - If you need to declare an argument but are not using it, prefix it's name - with an underscore. - - Encapsulate conditionals in a separate _well-named_ function, if possible - - Avoid negative conditionals; e.g., by making use of well-choosen names. - - Use Demorgan's rules: ~!x && !y ≣ !(x || y)~ and ~!x || !y ≣ !(x && y)~. - - Use ~try/catch~ with ~async/await~; or promises with both ~then~ and ~catch~. - - Don't ignore rejected promises, log it to external logging service - - Related chunks of code are clearly demarcated. - - If an anonymous function is too long, more than 2 lines, give it a name: - E.g., in JS, ~arr.map(x => ...) ≣ arr.map( function - doingSomeComplexStuff(x) { return ...} )~. The name aids in communicating - the intent, and is useful for debugging. - - [ES6] Braces are used for block scope, and not simulated using IIFEs. - - Avoid explicit newlines with ~+ "\n" +~ in-favour of Template Literals, which preserve line - breaks. - - Use Destructuring instead of explicit projections; aids in readability. - - Note∶ ~let y = x.y~ ≡ ~let {y} = x~ only holds when ~x~ is not ~null~ - (and so when ~x~ is not a expression involving ~?.~). +# +name: enable making init and readme +#+name: startup-code +#+begin_src emacs-lisp :tangle no :eval never-export + (defun my/make-init-el-and-README () + "Tangle an el and a github README from my init.org." + (interactive "P") ;; Places value of universal argument into: current-prefix-arg + (when current-prefix-arg + (let* ((time (current-time)) + (_date (format-time-string "_%Y-%m-%d")) + (.emacs "~/.emacs") + (.emacs.el "~/.emacs.el")) + ;; Make README.org + (save-excursion + (org-babel-goto-named-src-block "make-readme") ;; See next subsubsection. + (org-babel-execute-src-block)) + + ;; remove any other initialisation file candidates + (ignore-errors + (f-move .emacs (concat .emacs _date)) + (f-move .emacs.el (concat .emacs.el _date))) + + ;; Make init.el + (org-babel-tangle) + ;; (byte-compile-file "~/.emacs.d/init.el") + (load-file "~/.emacs.d/init.el") + + ;; Acknowledgement + (message "Tangled, compiled, and loaded init.el; and made README.md … %.06f seconds" + (float-time (time-since time)))))) - ,#+html:
+(add-hook 'after-save-hook 'my/make-init-el-and-README nil 'local-to-this-file-please) #+end_src -** COMMENT ⌘-e: Edit Everything in a separate buffer +**** The Org-block named =make-readme= :PROPERTIES: -:CUSTOM_ID: e-Edit-Everything-in-a-separate-buffer +:CUSTOM_ID: The-Org-block-named-make-readme :END: -Edit comment/string/docstring/code block in separate buffer with your favourite -mode. +Where the following block has ~#+NAME: make-readme~ before it. This source block +generates the ~README~ for the associated Github repository. +#+NAME: make-readme +#+begin_src emacs-lisp :tangle no :tangle no :export_never t +(save-buffer) +(with-temp-buffer + (insert + "#+EXPORT_FILE_NAME: README.org -+ ⌘-e to toggle “e”diting of thing at point. -+ ⌘-e will try to edit thing at point, or selection, if possible; otherwise it - will exit edit session. - - 🔥 Avoid escape nightmares by editing strings in a separate buffer. - - On a string, press ⌘-e and a new buffer pops up with unescaped content, - letting you edit raw strings directly. It then takes care of automatically - escaping strings for you when you press ⌘-e, or ~C-c C-c~ ---or discard your - changes with ~C-c C-k~. -+ ⌘-e can be used within edit session to create new edit sessions, if possible; then ⌘-e will “pop-off-the-stack” as expected. - - (I already use ⌘-e to toggle editing Org src blocks, and when editing a block ⌘-e will try to do a recursive edit if possible, or exit the edit session.) - - (The separedit package also let's me press ⌘-e to edit variable values when describing them, and to edit text when in the minibuffer.) + # Logos and birthday present painting + ,#+HTML:" (s-collapse-whitespace (concat + "

+ +

-From [[https://github.com/twlz0ne/separedit.el][source]]: -#+begin_example org -+----------+ Edit +-----------+ Edit +-----------+ -| | ---------------------> | edit | ---------------------> | edit | ... -| | point-at-comment? | buffer | point-at-comment? | buffer | -| source | point-at-string? | | point-at-string? | | ... -| buffer | point-at-codeblock? | (markdown | point-at-codeblock? | (markdown | ... -| | point-at-...? | orgmode | point-at-...? | orgmode | -| | <--------------------- | ...) | <--------------------- | ...) | ... -+----------+ Commit changes +-----------+ Commit changes +-----------+ -#+end_example +

+ + + +

-[In-general, we can invoke [[https://github.com/Fanael/edit-indirect/blob/e3d86416bcf8ddca951d7d112e57ad30c5f9a081/edit-indirect.el#L124][M-x edit-indirect-region]] to edit any selected piece -of text in its own buffer, then ~C-c C-c~ (or ~⌘-e~ with my setup) to commit the -edit. Actually, I've altered by ~⌘-e~ setup to also account for a selected region -😉] +

+ +

+ ")) -#+begin_src emacs-lisp -(use-package separedit :defer nil) -;; -;; # Example Usage -;; -;; 1. Press ⌘-e on this line, to edit this entire comment. -;; 2. Press ⌘-e to exit the edit session. -;; -;; Since my ⌘-e is context sensitive, to determine whether to continue editing or -;; exit; you can explicitly request an edit with C-c ' and an exit with C-c C-c. -;; -;; ``` -;; ;; 3. Press ⌘-e on this line, to edit this source block! -;; ;; 4. Press ⌘-e on this line, to edit this inner-most comment! -;; ;; 5. At start of next line, press “⌘-r ⌘-e” to edit just the source block -;; ;; -;; (cl-defun index (&rest args) -;; "6. Press ⌘-e to edit this string, \"7. and again in these quotes\"" -;; "

8. Press ⌘-e to edit this HTML block, in Web-mode

") -;; -;; ;; 9. Press C-u ⌘-e to guess the language of the next string (Rust); then ⌘-r C-c C-r to quickly run the code. -;; "fn main() { println!(\"{}\", \"hello!\"); }" -;; -;; ;; 10. Select & press “C-u ⌘-e” on the following, to edit it in whatever mode you want. -;; ;; select * from table -- Or just press ⌘-e and have the mode detected. -;; -;; ``` + ;; My Literate Setup; need the empty new lines for the export + " -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Setup to make the above ⌘-e behaviour happen. + I enjoy reading others' /literate/ configuration files and + incorporating what I learn into my own. The result is a + sufficiently well-documented and accessible read that yields + a stylish and functional system (•̀ᴗ•́)و -;; Make “⌘-e” toggle editing string literals / select region / [Org/markdown] code block / comment block when programming. -(--map (bind-key "s-e" #'separedit it) - '(prog-mode-map minibuffer-local-map help-mode-map)) ;; TODO: helpful-mode-map + This ~README.org~ has been automatically generated from my + configuration and its contents below are accessible + in (outdated) blog format, with /colour/, or as colourful + PDF, [[https://alhassy.github.io/init/][here]]. Enjoy + :smile: + + ,#+INCLUDE: init.org + ") + ;; No code execution on export + ;; ⟪ For a particular block, we use “:eval never-export”. ⟫ + (let ((org-export-use-babel nil)) + (org-mode) + (org-org-export-to-org))) #+end_src +Alternatively, evaluate the above source block with ~C-c C-c~ to produce a ~README~ +file. -#+begin_src emacs-lisp -;; TODO:Merge these changes upstream +For the ‘badges’, see https://shields.io/. The syntax above is structured: +#+begin_example org +https://img.shields.io/badge/