@@ -3521,6 +3521,10 @@ Usage:
35213521(bind-key* " C-c c" (def-capture " Inbox Entry 📩" " Inbox 📩 \t\t\t :inbox:" " * TODO %?\n :PROPERTIES:\n :CREATED: %U\n :END:\n " ))
35223522; ; Capture: Now that I know how to query my agenda, how do I get things into it efficiently?:1 ends here
35233523
3524+ # [[file:init.org::*Why the evening ritual reduces anxiety?] [Why the evening ritual reduces anxiety?: 1]]
3525+ *** End-of-workday Shut-down Ritual: ~C-c r d~ <2025-04-14 Mon 15:30-15:45 +1d>
3526+ # Why the evening ritual reduces anxiety?: 1 ends here
3527+
35243528; ; [[file:init.org::*Questionnaire setup][Questionnaire setup:1]]
35253529(require 'eieio )
35263530
@@ -3667,7 +3671,9 @@ and 2 of them are randomly selected as part of the daily review.
36673671(defun my/read-daily-review-properties ()
36683672 " Returns a list of (PROPERTY . VALUE) pairs that could be `org-set-property' on a headline.
36693673
3670- Makes use of `my/daily-review-questionnaire' ."
3674+ Makes use of `my/daily-review-questionnaire' .
3675+
3676+ At any time, press `C-.' to toggle adding a customised explanatory note to go along with a selection."
36713677 (-let [(mandatory-questions random-questions) (-split-on :random my/daily-review-questionnaire)]
36723678 (let* ((max-possible-score 0 )
36733679 (properties
@@ -3684,19 +3690,30 @@ Makes use of `my/daily-review-questionnaire'."
36843690 (if is-open-ended?
36853691 (read-from-minibuffer prompt)
36863692 (cl-incf max-possible-score (apply #'max (mapcar #'my/option-score options)))
3687- (consult--read (--map (my/option-label it) options)
3688- :prompt prompt
3689- :require-match t
3690- :annotate (lambda (label )
3691- (format " \t ⟨ %s ⟩" (my/option-description (assoc-by-label options label))))
3692- :lookup (lambda (label _ _ _ )
3693- (-let [option (assoc-by-label options label)]
3694- ; ; If label ends in “…”, prompt for a note.
3695- (when (s-ends-with? " …" label)
3696- (-let [note (s-trim (read-from-minibuffer " Care to elaborate? [ENTER to skip] " ))]
3697- (unless (s-blank? note)
3698- (setf (my/option-description option) note))))
3699- (pretty-print option)))))))))
3693+ ; ; If the user presses “C-.” they toggle on “note entry”.
3694+ (let (note-has-been-requested
3695+ (my/note-map (make-sparse-keymap )))
3696+ (define-key my/note-map (kbd " C-." )
3697+ (lambda () (interactive )
3698+ (setq note-has-been-requested (not note-has-been-requested))
3699+ (message (if note-has-been-requested " [You can enter a note after making a selection!]"
3700+ " [No entry note will be requested after selection.]" ))))
3701+ (set-keymap-parent my/note-map minibuffer-local-map) ; ; So that ⟨ENTER⟩ finalises the minibuffer, and not a literal new line!
3702+ (minibuffer-with-setup-hook
3703+ (lambda () (use-local-map (copy-keymap my/note-map)))
3704+ (consult--read (--map (my/option-label it) options)
3705+ :prompt prompt
3706+ :require-match t
3707+ :annotate (lambda (label )
3708+ (format " \t ⟨ %s ⟩" (my/option-description (assoc-by-label options label))))
3709+ :lookup (lambda (label _ _ _ )
3710+ (-let [option (assoc-by-label options label)]
3711+ ; ; If label ends in “…” or “C-.” pressed, prompt for a note.
3712+ (when (or (s-ends-with? " …" label) note-has-been-requested)
3713+ (-let [note (s-trim (read-from-minibuffer " Enter an explanatory note [ENTER to skip] " ))]
3714+ (unless (s-blank? note)
3715+ (setf (my/option-description option) note))))
3716+ (pretty-print option)))))))))))
37003717 ; ; Prepend a computed “daily score” property. Hopefully this value increases with time.
37013718 (cons
37023719 (thread-last properties
@@ -3763,7 +3780,7 @@ Further reading:
37633780
37643781 (insert (my/fancy-date-string) " " )
37653782 ; ; Let's add some properties by prompting the user, me.
3766- (-let [properties (when nil my/read-daily-review-properties)]
3783+ (-let [properties (my/read-daily-review-properties)]
37673784 (cl-loop for (property . value) in properties
37683785 do (org-set-property property value))
37693786 ; ; Add Daily Score to the start of the headline
@@ -3819,10 +3836,13 @@ Further reading:
38193836
38203837 (insert " \n #+end_stats_of_the_day\n\n " )
38213838
3839+ (my/show-life-purpose-statement-then-remove-it-after-I-read-it)
38223840
38233841 (insert " \n 💬" (my/word-of-the-day))
3842+ (org-fill-paragraph )
38243843
38253844 ; ; I think it'd be neat to insert my clocked-in / logs of the day here.
3845+ ; ; Look at what I clocked into this day/week! Get a great idea of what I've done with my time, in detail. Also, see ~C-c a v L~.
38263846 (save-excursion ; ; I want cursor to stay here.
38273847 (let (todays-agenda org-agenda-finalize-hook)
38283848 (org-agenda-list 1 )
@@ -3831,8 +3851,12 @@ Further reading:
38313851 (org-agenda-quit)
38323852 (insert " \n\n #+begin_agenda_for_the_day\n " )
38333853 (insert todays-agenda)
3834- (insert " #+end_agenda_for_the_day\n " )
3835- (insert " ⟨🤔 Did I get everything I wanted done? Perhaps, I underestimated time for things? 🗯️⟩" )))
3854+ (insert " #+end_agenda_for_the_day" )
3855+ (insert " \n ⟨🤔 Did I get everything I wanted done? Perhaps, I underestimated time for things? 🗯️⟩" )
3856+ (insert " \n ~C-c a w v c~ to check for time gaps and review time for the past week. And to see what I worked on, and where I spent too much time or too little." )))
3857+ (insert " \n Say, “Today, my purpose was to have fun and do a good job at work! I did it! (。◕‿◕。)”" )
3858+
3859+
38363860 ; ;
38373861 ; ; MA: Consider adding other journal prompts here, whose replies may be long-form.
38383862 ; ; E.g., pick one, or two, random prompts.
@@ -3841,6 +3865,29 @@ Further reading:
38413865
38423866
38433867
3868+
3869+
3870+ (cl-defun my/show-life-purpose-statement-then-remove-it-after-I-read-it ()
3871+ (set-mark-command nil )
3872+ (-let [purpose " When people say “What are you doing?”,
3873+ You say ⟪“Things that please me.”⟫
3874+ They say “Toward what end?”,
3875+ and you say ⟪“Pleasure.”⟫
3876+ They say “But really, what are you working on?”
3877+ You say ⟪“Having a good time!”⟫
3878+ " ]
3879+ ; ; NOTE: explore more faces via M-x highlight-phrase.
3880+ (insert (propertize purpose 'font-lock-face 'hi-green )))
3881+ (while (not (equal " yes"
3882+ (consult--read '(" yes" " no" ) :prompt " Read “Life Purpose”?"
3883+ :annotate (lambda (it ) (format " ⟨%s ⟩ "
3884+ (if (equal it " no" )
3885+ " C'mon man, read it"
3886+ " That's right, live the good life!" )))))))
3887+ (backward-delete-char 1 ))
3888+
3889+
3890+
38443891(defun my/word-of-the-day ()
38453892 (let (result)
38463893 (org-web-tools-read-url-as-org " https://www.merriam-webster.com/word-of-the-day" )
@@ -3993,16 +4040,17 @@ Further reading:
39934040(load-file " ~/Dropbox/private.el" ) ; ; Loads “my\⋯” variables
39944041; ;
39954042(cl-defun my/age-in-days-weeks-years (&optional (birthdate my\b irthday))
3996- " Prompt for birthdate (YYYY-MM-DD) and display age in days, weeks, and years."
4043+ " Prompt for birthdate (YYYY-MM-DD) and display age in days, weeks, months, and years."
39974044 (interactive )
39984045 (let* ((birth-time (date-to-time (concat birthdate " 00:00:00" )))
39994046 (now (current-time ))
40004047 (days-old (/ (float-time (time-subtract now birth-time)) 86400 ))
40014048 (weeks-old (/ days-old 7 ))
4049+ (months-old (/ days-old 30.44 )) ; ; average month length
40024050 (years-old (/ days-old 365.25 ))) ; ; approximate year with leap years
4003- (cl-format nil " I am now ~:d days old, which is ~:d weeks old, which is ~,1f years old"
4004- (floor days-old) (floor weeks-old) years-old)))
4005- ; ;
4051+ (cl-format nil " I am now ~:d days old; which is ~:d weeks old; which is ~:d months old; which is ~ ,1f years old. "
4052+ (floor days-old) (floor weeks-old) ( floor months-old) years-old)))
4053+ ; ;
40064054; ; Elisp's “format” is not as capable as Common Lisp's “format”.
40074055; ; E.g., there's no equivalent of (cl-format nil "~:d" 1000000)
40084056; ; which prints numbers with comma separators.
@@ -4106,7 +4154,9 @@ TODO:
41064154 (cl-defun my/yank-html-media (_media-type contents )
41074155 (insert (s-replace " " " " (shell-command-to-string
41084156 (-let [delimiter " EOF" ] ; ; A unique “here-document delimiter”, unlikely to be part of ‘contents’
4109- (format " pandoc -f html -t org <<%s \n %s \n %s " delimiter contents delimiter)))))))
4157+ ; ; NOTE: `shell-quote-argument` does too much here; e.g., copied code blocks wont paste nicely.
4158+ ; ; For now, I want $FOO to be pasted as itself, and not interpreted as a Shell variable.
4159+ (format " pandoc -f html -t org <<%s \n %s \n %s " delimiter (s-replace " $" " \\ $" contents) delimiter)))))))
41104160
41114161 (yank-media-handler
41124162 " STRING"
@@ -4893,3 +4943,121 @@ method."
48934943
48944944 )
48954945; ; Testing that things are as they should be:1 ends here
4946+
4947+ ; ; [[file:init.org::*Hyperbole: “DWIM at point”][Hyperbole: “DWIM at point”:1]]
4948+ (use-package hyperbole)
4949+ (hyperbole-mode +1 )
4950+ (setq hsys-org-enable-smart-keys t )
4951+ ; ; Hyperbole: “DWIM at point”:1 ends here
4952+
4953+ ; ; [[file:init.org::*~MyModule::72~ means “find the file named ~MyModule~, somewhere, and jump to line 72”][~MyModule::72~ means “find the file named ~MyModule~, somewhere, and jump to line 72”:1]]
4954+ (defun my/open-::-file-path (path )
4955+ " PATH is something like FooModule::72 or FooModule::interface_bar"
4956+ ; ; (message-box path)
4957+ (-let [(file regex) (s-split " ::" path)]
4958+ ; ; brew install fd
4959+ ; ; NOTE: fd is fast!
4960+ (-let [results (shell-command-to-string (format " fd \" ^%s \\ ..*$\" %s " file my\w ork-dir))]
4961+ ; ; (message-box results)
4962+ (find-file (car (s-split " \n " results)))
4963+ (-let [line (string-to-number regex)]
4964+ (if (= 0 line)
4965+ (progn (beginning-of-buffer ) ; ; In case file already open
4966+ (re-search-forward (s-replace " _" " " regex) nil t ))
4967+ (goto-line line))))))
4968+
4969+ (defib my/::-file-paths ()
4970+ " Find the file whose name is at point and jump to the given regex or line number."
4971+ (let ((case-fold-search t )
4972+ (path-id nil )
4973+ (my-regex " \\ b\\ (\\ w+::[^ ]+\\ )" ))
4974+ (if (or (looking-at my-regex)
4975+ (save-excursion
4976+ (my/move-to-::-phrase-start)
4977+ (looking-at my-regex)))
4978+ (progn (setq path-id (match-string-no-properties 1 ))
4979+ (ibut:label-set path-id
4980+ (match-beginning 1 )
4981+ (match-end 1 ))
4982+ (hact 'my/open-::-file-path path-id)))))
4983+
4984+
4985+ (defun my/move-to-::-phrase-start ()
4986+ " Move cursor to the start of a phrase like MyFile::13."
4987+ (interactive )
4988+ (let ((case-fold-search t )
4989+ (pattern " \\ b\\ (\\ w+::[^ ]+\\ )" )
4990+ (max-lookback 20 ) ; Maximum distance to look back
4991+ (pos (point )))
4992+ (catch 'found
4993+ (while (and (> pos (point-min ))
4994+ (<= (- pos (point )) max-lookback))
4995+ (goto-char pos)
4996+ (when (looking-at pattern)
4997+ (goto-char (match-beginning 0 ))
4998+ (throw 'found t ))
4999+ (setq pos (1- pos))))))
5000+
5001+ ; ; Some highlighting so I'm prompted to use “M-RET”
5002+ (font-lock-add-keywords
5003+ 'org-mode
5004+ '((" \\ b[^ ]*::[^ ]*" 0 'highlight prepend))
5005+ t )
5006+ ; ; ~MyModule::72~ means “find the file named ~MyModule~, somewhere, and jump to line 72”:1 ends here
5007+
5008+ ; ; [[file:init.org::*Fontify Org Radio Targets and have ~M-RET~ Jump to Them][Fontify Org Radio Targets and have ~M-RET~ Jump to Them:1]]
5009+ (defun get-radio-targets ()
5010+ " Extract all radio targets from the current Org buffer"
5011+ (interactive )
5012+ (let ((targets nil )
5013+ (case-fold-search t ))
5014+ (cl-loop for file in (cons " ~/.emacs.d/init.org" org-agenda-files)
5015+ do (save-excursion
5016+ (find-file file)
5017+ (save-restriction
5018+ (widen )
5019+ (goto-char (point-min ))
5020+ (while (re-search-forward " <<<\\ (.*?\\ )>>>" nil t )
5021+ (push (list (downcase (substring-no-properties (match-string 1 ))) file (line-number-at-pos )) targets)))))
5022+ targets))
5023+
5024+ (setq my/radio-targets (get-radio-targets))
5025+ (setq my/radio-regex (eval `(rx (or ,@(mapcar #'cl-first my/radio-targets)))))
5026+
5027+ (font-lock-add-keywords
5028+ 'org-mode
5029+ (--map (list (cl-first it) 0 ''highlight 'prepend ) my/radio-targets)
5030+ t )
5031+
5032+
5033+ ; ; In programming modes, just show an underline.
5034+ (add-hook
5035+ 'prog-mode-hook
5036+ (lambda ()
5037+ (font-lock-add-keywords
5038+ nil
5039+ (--map (list (cl-first it) 0 ''(:underline t ) 'prepend ) my/radio-targets)
5040+ t )))
5041+
5042+
5043+ (defun my/jump-to-radio (radio )
5044+ " RADIO is a downcased name."
5045+ (-let [(name file line) (assoc radio my/radio-targets)]
5046+ (find-file file)
5047+ (goto-line line)))
5048+
5049+
5050+ (defib my/radio-target ()
5051+ " Jump to the definition of this word, as an Org radio target"
5052+ (let ((case-fold-search t )
5053+ (radio nil ))
5054+ (if (or (looking-at my/radio-regex)
5055+ (save-excursion
5056+ (re-search-backward " \\ b" )
5057+ (looking-at my/radio-regex)))
5058+ (progn (setq radio (downcase (match-string-no-properties 0 )))
5059+ (ibut:label-set radio
5060+ (match-beginning 0 )
5061+ (match-end 0 ))
5062+ (hact 'my/jump-to-radio radio)))))
5063+ ; ; Fontify Org Radio Targets and have ~M-RET~ Jump to Them:1 ends here
0 commit comments