mirror of
https://github.com/SqrtMinusOne/sqrtminusone.github.io.git
synced 2025-12-10 15:53:03 +03:00
Autocommit 2021-09-03T13:44+05:00
Hostname: eminence
This commit is contained in:
parent
78150f2c3e
commit
fce34a425d
7 changed files with 498 additions and 23 deletions
|
|
@ -937,7 +937,7 @@ fi
|
|||
```
|
||||
|
||||
|
||||
### `autocommmit` {#autocommmit}
|
||||
### `autocommit` {#autocommit}
|
||||
|
||||
A script to autocommit files in a repository. I use it to sync my org directory and password store. I guess it's not how git is intended to be used, but it works for me.
|
||||
|
||||
|
|
|
|||
|
|
@ -897,7 +897,7 @@ The script below allows me to:
|
|||
```bash
|
||||
hostname=$(hostname)
|
||||
# Settings varying on the hostname
|
||||
export WLAN_INTERFACE=$(nmcli -f DEVICE con show | grep -Ev "(.*docker.*|DEVICE|br.*|tun.*|--)" | xargs)
|
||||
export WLAN_INTERFACE=$(nmcli -f DEVICE con show | grep -Ev "(.*docker.*|DEVICE|br.*|tun.*|veth.*|--)" | xargs)
|
||||
if [ "$hostname" = "azure" ]; then
|
||||
TRAY_MONITOR="eDP-1"
|
||||
# export WLAN_INTERFACE="wlp3s0"
|
||||
|
|
@ -936,7 +936,7 @@ declare -A BLOCKS=(
|
|||
)
|
||||
|
||||
# Geolocation for some modules
|
||||
export LOC="SPB"
|
||||
export LOC="TMN"
|
||||
|
||||
export IPSTACK_API_KEY=$(pass show My_Online/APIs/ipstack | head -n 1)
|
||||
|
||||
|
|
@ -2346,6 +2346,7 @@ This section generates manifests for various desktop software that I'm using.
|
|||
| dev | node |
|
||||
| dev | openjdk |
|
||||
| dev | go |
|
||||
| dev | gcc-toolchain |
|
||||
|
||||
|
||||
### Manifests {#manifests}
|
||||
|
|
|
|||
|
|
@ -124,6 +124,12 @@ As with other files in the repo, parts prefixed with (OFF) are not used but kept
|
|||
- [Productivity & Knowledge management](#productivity-and-knowledge-management)
|
||||
- [Capture templates & various settings](#capture-templates-and-various-settings)
|
||||
- [Custom agendas](#custom-agendas)
|
||||
- [org-ql](#org-ql)
|
||||
- [Review workflow](#review-workflow)
|
||||
- [Data from git & org-roam](#data-from-git-and-org-roam)
|
||||
- [Data from org-journal](#data-from-org-journal)
|
||||
- [Data from org-agenda via org-ql](#data-from-org-agenda-via-org-ql)
|
||||
- [Capture template](#capture-template)
|
||||
- [Org Journal](#org-journal)
|
||||
- [Org Roam](#org-roam)
|
||||
- [org-roam-ui](#org-roam-ui)
|
||||
|
|
@ -234,7 +240,7 @@ As with other files in the repo, parts prefixed with (OFF) are not used but kept
|
|||
- [Notmuch](#notmuch)
|
||||
- [Elfeed](#elfeed)
|
||||
- [Some additions](#some-additions)
|
||||
- [YouTube](#youtube)
|
||||
- [YouTube & EMMS](#youtube-and-emms)
|
||||
- [EMMS](#emms)
|
||||
- [MPD](#mpd)
|
||||
- [MPV](#mpv)
|
||||
|
|
@ -2966,6 +2972,262 @@ Log DONE time
|
|||
```
|
||||
|
||||
|
||||
#### org-ql {#org-ql}
|
||||
|
||||
[org-ql](https://github.com/alphapapa/org-ql) is a package to query the org files. I'm using it in my review workflow, perhaps later I'll find another usecases.
|
||||
|
||||
```emacs-lisp
|
||||
(use-package org-ql
|
||||
:straight (:fetcher github
|
||||
:repo "alphapapa/org-ql"
|
||||
:files (:defaults (:exclude "helm-org-ql.el"))))
|
||||
```
|
||||
|
||||
|
||||
#### Review workflow {#review-workflow}
|
||||
|
||||
My take on a review workflow. As a baseline, I want to have a template that lists the important changes since the last review and other basic information. I'm doing reviews regularly, but the time intervals still may vary, hence this flexibility.
|
||||
|
||||
|
||||
##### Data from git & org-roam {#data-from-git-and-org-roam}
|
||||
|
||||
First, as I have [autocommit]({{< relref "Console" >}}) set up in my org directory, here is a handy function to get an alist of changed files of a form `(status . path)`. In principle, the `rev` parameter can be a commit, tag, etc but here I'm interested in a form like `@{2021-08-30}`.
|
||||
|
||||
```emacs-lisp
|
||||
(setq my/git-diff-status
|
||||
'(("A" . added)
|
||||
("C" . copied)
|
||||
("D" . deleted)
|
||||
("M" . modified)
|
||||
("R" . renamed)
|
||||
("T" . type-changed)
|
||||
("U" . unmerged)))
|
||||
|
||||
(defun my/get-files-status (rev)
|
||||
(let ((files (shell-command-to-string (concat "git diff --name-status " rev))))
|
||||
(mapcar
|
||||
(lambda (file)
|
||||
(let ((elems (split-string file "\t")))
|
||||
(cons
|
||||
(cdr (assoc (car elems) my/git-diff-status))
|
||||
(nth 1 elems))))
|
||||
(split-string files "\n" t))))
|
||||
```
|
||||
|
||||
I'll use it to get a list of added and changed Roam files since the last review. Date should have be in a format `YYYY-MM-DD`.
|
||||
|
||||
```emacs-lisp
|
||||
(defun my/org-changed-files-since-date (date)
|
||||
(let ((default-directory org-directory))
|
||||
(my/get-files-status (format "@{%s}" date))))
|
||||
```
|
||||
|
||||
Now we are ready to format this list to insert it into the capture template.
|
||||
|
||||
```emacs-lisp
|
||||
(defun my/org-review-format-roam (rev)
|
||||
(let* ((changes (my/org-changed-files-since-date rev))
|
||||
(new-roam
|
||||
(seq-filter
|
||||
(lambda (elem)
|
||||
(and (eq (car elem) 'added)
|
||||
(string-match-p (rx bos "roam") (cdr elem))))
|
||||
changes))
|
||||
(changed-roam
|
||||
(seq-filter
|
||||
(lambda (elem)
|
||||
(and (eq (car elem) 'modified)
|
||||
(string-match-p (rx bos "roam") (cdr elem))))
|
||||
changes)))
|
||||
(concat
|
||||
(unless (seq-empty-p new-roam)
|
||||
(concat "** New Roam entries \n"
|
||||
(mapconcat
|
||||
(lambda (entry)
|
||||
(format "- [[file:%s][%s]]" (cdr entry) (cdr entry)))
|
||||
new-roam
|
||||
"\n")
|
||||
"\n"))
|
||||
(unless (seq-empty-p changed-roam)
|
||||
(concat "** Changed Roam entries \n"
|
||||
(mapconcat
|
||||
(lambda (entry)
|
||||
(format "- [[file:%s][%s]]" (cdr entry) (cdr entry)))
|
||||
changed-roam
|
||||
"\n"))))))
|
||||
```
|
||||
|
||||
|
||||
##### Data from org-journal {#data-from-org-journal}
|
||||
|
||||
Second, I want to have a list of new jounal entries since the last review.
|
||||
|
||||
```emacs-lisp
|
||||
(defun my/org-journal-entries-since-date (rev-date)
|
||||
(mapcar
|
||||
(lambda (date)
|
||||
(let ((time (encode-time (parse-time-string date))))
|
||||
`((file . ,(org-journal--get-entry-path time))
|
||||
(header . ,(format-time-string org-journal-date-format time)))))
|
||||
(seq-filter
|
||||
(lambda (date) (string-lessp rev-date date))
|
||||
(mapcar
|
||||
(lambda (date)
|
||||
(format "%04d-%02d-%02dT00:00:00+0300" (nth 2 date) (nth 0 date) (nth 1 date)))
|
||||
(org-journal--list-dates)))))
|
||||
```
|
||||
|
||||
Format the results:
|
||||
|
||||
```emacs-lisp
|
||||
(defun my/org-review-format-journal (rev-date)
|
||||
(mapconcat
|
||||
(lambda (item)
|
||||
(format "- [[file:%s::*%s][%s]]"
|
||||
(cdr (assoc 'file item))
|
||||
(cdr (assoc 'header item))
|
||||
(cdr (assoc 'header item))))
|
||||
(my/org-journal-entries-since-date rev-date)
|
||||
"\n"))
|
||||
```
|
||||
|
||||
|
||||
##### Data from org-agenda via org-ql {#data-from-org-agenda-via-org-ql}
|
||||
|
||||
Third, I want to list some changes in my agenda. This section will change depending on what I'm currently working on.
|
||||
|
||||
So, here is a list of queries results of which I want to see in the review template. The format is `(name date-field order-by-field query)`.
|
||||
|
||||
```emacs-lisp
|
||||
(setq my/org-ql-review-queries
|
||||
`(("Waitlist" scheduled scheduled
|
||||
(and
|
||||
(done)
|
||||
(tags-inherited "waitlist")))
|
||||
("Personal tasks done" closed ,nil
|
||||
(and
|
||||
(tags-inherited "personal")
|
||||
(todo "DONE")))
|
||||
("Attended meetings" closed scheduled
|
||||
(and
|
||||
(tags "meeting")
|
||||
(todo "PASSED")))
|
||||
("Done project tasks" closed deadline
|
||||
(and
|
||||
(todo "DONE")
|
||||
(ancestors
|
||||
(heading "Tasks"))))))
|
||||
```
|
||||
|
||||
The query will be executed like this: `(and (date-field :from rev-date) query)`
|
||||
|
||||
```emacs-lisp
|
||||
(defun my/org-review-exec-ql (saved rev-date)
|
||||
(let ((query `(and
|
||||
(,(nth 1 saved) :from ,rev-date)
|
||||
,(nth 3 saved))))
|
||||
(org-ql-query
|
||||
:select #'element
|
||||
:from (org-agenda-files)
|
||||
:where query
|
||||
:order-by (nth 2 saved))))
|
||||
```
|
||||
|
||||
Format one element of query result.
|
||||
|
||||
```emacs-lisp
|
||||
(defun my/org-review-format-element (elem)
|
||||
(concat
|
||||
(string-pad
|
||||
(plist-get (cadr elem) :raw-value)
|
||||
40)
|
||||
(when-let (scheduled (plist-get (cadr elem) :scheduled))
|
||||
(concat " [SCHEDULED: " (plist-get (cadr scheduled) :raw-value) "]"))
|
||||
(when-let (deadline (plist-get (cadr elem) :deadline))
|
||||
(concat " [DEADLINE: " (plist-get (cadr deadline) :raw-value) "]"))))
|
||||
```
|
||||
|
||||
Execute all the saved queries and format an Org list for the capture template.
|
||||
|
||||
```emacs-lisp
|
||||
(defun my/org-review-format-queries (rev-date)
|
||||
(mapconcat
|
||||
(lambda (results)
|
||||
(concat "** " (car results) "\n"
|
||||
(string-join
|
||||
(mapcar (lambda (r) (concat "- " r)) (cdr results))
|
||||
"\n")
|
||||
"\n"))
|
||||
(seq-filter
|
||||
(lambda (result)
|
||||
(not (seq-empty-p (cdr result))))
|
||||
(mapcar
|
||||
(lambda (saved)
|
||||
(cons
|
||||
(car saved)
|
||||
(mapcar
|
||||
#'my/org-review-format-element
|
||||
(my/org-review-exec-ql saved rev-date))))
|
||||
my/org-ql-review-queries))
|
||||
"\n"))
|
||||
```
|
||||
|
||||
|
||||
##### Capture template {#capture-template}
|
||||
|
||||
Now, we have to put all this together and define a capture template for the review.
|
||||
|
||||
I'll use a separate directory for the review files, just like for org-journal and org-roam. The filename will have a format `YYYY-MM-DD.org`, which will also free me from the effort of storing the last review date somewhere.
|
||||
|
||||
If somehow there are no files in the folder, fallback to the current date minus one week.
|
||||
|
||||
```emacs-lisp
|
||||
(setq my/org-review-directory "review")
|
||||
|
||||
(defun my/org-review-get-filename ()
|
||||
(concat my/org-review-directory "/" (format-time-string "%Y-%m-%d.org" (current-time))))
|
||||
|
||||
(defun my/get-last-review-date ()
|
||||
(substring
|
||||
(or
|
||||
(-max-by
|
||||
'string-greaterp
|
||||
(-filter
|
||||
(lambda (f) (not (or (string-equal f ".") (string-equal f ".."))))
|
||||
(directory-files (f-join org-directory my/org-review-directory))))
|
||||
(format-time-string
|
||||
"%Y-%m-%d"
|
||||
(time-subtract
|
||||
(current-time)
|
||||
(seconds-to-time (* 60 60 24 7)))))
|
||||
0 10))
|
||||
```
|
||||
|
||||
A template looks like this:
|
||||
|
||||
```emacs-lisp
|
||||
(setq my/org-review-capture-template
|
||||
`("r" "Review" plain (file ,(my/org-review-get-filename))
|
||||
,(string-join
|
||||
'("#+TITLE: Review %t"
|
||||
""
|
||||
"Last review date: %(org-timestamp-translate (org-timestamp-from-string (format \"<%s>\" (my/get-last-review-date))))"
|
||||
""
|
||||
"* Roam"
|
||||
"%(my/org-review-format-roam (my/get-last-review-date))"
|
||||
"* Journal"
|
||||
"New journal entries:"
|
||||
"%(my/org-review-format-journal (my/get-last-review-date))"
|
||||
"* Agenda"
|
||||
"%(my/org-review-format-queries (my/get-last-review-date))"
|
||||
"* Thoughts :crypt:"
|
||||
"%?")
|
||||
"\n")))
|
||||
|
||||
(add-to-list 'org-capture-templates my/org-review-capture-template t)
|
||||
```
|
||||
|
||||
|
||||
#### Org Journal {#org-journal}
|
||||
|
||||
[org-journal](https://github.com/bastibe/org-journal) is a plugin for maintaining a journal in org mode. I want to have its entries separate from my knowledge base.
|
||||
|
|
@ -4101,8 +4363,7 @@ Trying this one out instead of vue-mode and svelte-mode, because this one seems
|
|||
:config
|
||||
(add-hook 'web-mode-hook 'smartparens-mode)
|
||||
(add-hook 'web-mode-hook 'hs-minor-mode)
|
||||
(my/set-smartparens-indent 'web-mode)
|
||||
(add-hook 'web-mode-hook ))
|
||||
(my/set-smartparens-indent 'web-mode))
|
||||
```
|
||||
|
||||
Hooking this up with lsp.
|
||||
|
|
@ -5338,7 +5599,7 @@ Open a URL with eww.
|
|||
```
|
||||
|
||||
|
||||
##### YouTube {#youtube}
|
||||
##### YouTube & EMMS {#youtube-and-emms}
|
||||
|
||||
Previously this block was opening MPV with `start-process`, but now I've managed to hook up MPV with EMMS. So there is the EMMS+elfeed "integration".
|
||||
|
||||
|
|
@ -5364,12 +5625,13 @@ Now, a function to add YouTube link with metadata from elfeed to EMMS.
|
|||
(let ((track (emms-track
|
||||
'url (my/get-youtube-url (elfeed-entry-link entry)))))
|
||||
(emms-track-set track 'info-title (elfeed-entry-title entry))
|
||||
(setq my/test track)
|
||||
(emms-playlist-insert-track track))))
|
||||
|
||||
(defun my/elfeed-add-emms-youtube ()
|
||||
(interactive)
|
||||
(emms-add-elfeed elfeed-show-entry))
|
||||
(emms-add-elfeed elfeed-show-entry)
|
||||
(elfeed-tag elfeed-show-entry 'watched)
|
||||
(elfeed-show-refresh))
|
||||
|
||||
(with-eval-after-load 'elfeed
|
||||
(general-define-key
|
||||
|
|
|
|||
|
|
@ -1552,7 +1552,7 @@ Before I figure out how to package this for Guix:
|
|||
:straight (:host github :repo "SqrtMinusOne/wakatime-mode")
|
||||
:if (not my/is-termux)
|
||||
:config
|
||||
<<wakatime-fixes>>
|
||||
(setq wakatime-ignore-exit-codes '(0 1 102))
|
||||
(advice-add 'wakatime-init :after (lambda () (setq wakatime-cli-path "/home/pavel/bin/wakatime-cli")))
|
||||
;; (setq wakatime-cli-path (executable-find "wakatime"))
|
||||
(global-wakatime-mode))
|
||||
|
|
@ -4028,7 +4028,7 @@ My bit of config here:
|
|||
:straight t
|
||||
:init
|
||||
(my-leader-def
|
||||
:keymaps '(js-mode-map typescript-mode-map vue-mode-map svelte-mode-map)
|
||||
:keymaps '(js-mode-map web-mode-map typescript-mode-map vue-mode-map svelte-mode-map)
|
||||
"rr" #'prettier-prettify))
|
||||
```
|
||||
|
||||
|
|
@ -4101,7 +4101,8 @@ Trying this one out instead of vue-mode and svelte-mode, because this one seems
|
|||
:config
|
||||
(add-hook 'web-mode-hook 'smartparens-mode)
|
||||
(add-hook 'web-mode-hook 'hs-minor-mode)
|
||||
(my/set-smartparens-indent 'web-mode))
|
||||
(my/set-smartparens-indent 'web-mode)
|
||||
(add-hook 'web-mode-hook ))
|
||||
```
|
||||
|
||||
Hooking this up with lsp.
|
||||
|
|
@ -4120,6 +4121,16 @@ Hooking this up with lsp.
|
|||
(add-hook 'web-mode-hook #'my/web-mode-lsp)
|
||||
```
|
||||
|
||||
Vue settings
|
||||
|
||||
```emacs-lisp
|
||||
(defun my/web-mode-vue-setup ()
|
||||
(when (string-match-p (rx ".vue" eos) (buffer-name))
|
||||
(setq-local web-mode-script-padding 0)))
|
||||
|
||||
(add-hook 'web-mode-hook 'my/web-mode-vue-setup)
|
||||
```
|
||||
|
||||
|
||||
#### <span class="org-todo done OFF">OFF</span> (OFF) Vue.js {#off--vue-dot-js}
|
||||
|
||||
|
|
@ -4763,7 +4774,7 @@ For some reason it doesn't use pipenv python executable, so here is a small work
|
|||
(if asc
|
||||
(cdr asc)
|
||||
(let ((python-executable
|
||||
(string-trim (shell-command-to-string "PIPENV_IGNORE_VIRTUALENVS=1 pipenv run which python"))))
|
||||
(string-trim (shell-command-to-string "PIPENV_IGNORE_VIRTUALENVS=1 pipenv run which python 2>/dev/null"))))
|
||||
(if (string-match-p ".*not found.*" python-executable)
|
||||
(message "Pipfile found, but not pipenv executable!")
|
||||
(message (format "Found pipenv python: %s" python-executable))
|
||||
|
|
@ -5469,7 +5480,7 @@ After all this is done, run `M-x emms-cache-set-from-mpd-all` to set cache from
|
|||
[mpv](https://mpv.io/) is a decent media player, which has found a place in this configuration because it integrates with youtube-dl.
|
||||
|
||||
```emacs-lisp
|
||||
(add-to-list 'emms-player-list 'emms-player-mpv t)
|
||||
(add-to-list 'emms-player-list 'emms-player-mpv)
|
||||
```
|
||||
|
||||
Also a custom regex. My demands for MPV include running `youtube-dl`, so there is a regex that matches youtube.com or some of the video formats.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ The majority of the software is configured with [literate configuration](https:/
|
|||
|
||||
The files themselves are managed and deployed via [yadm](https://yadm.io/), but I mostly use Org Mode rich noweb whenever I can instead of what yadm offers.
|
||||
|
||||
My current GNU/Linux distribution is [GNU Guix](https://guix.gnu.org/). In the context of this repo, Guix allows me to list all the used programs in manifests, which means I have the same set of programs across multiple machines. Looks for Org tables with "Guix dependency" in the header.
|
||||
My current GNU/Linux distribution is [GNU Guix](https://guix.gnu.org/). In the context of this repo, Guix allows me to list all the used programs in manifests, which means I have the same set of programs across multiple machines. Look for the tables with "Guix dependency" in the header.
|
||||
|
||||
Literate configuration files:
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ Some of the notable programs are listed in the table below.
|
|||
|
||||
## Some statistics {#some-statistics}
|
||||
|
||||
Run the following to show the pictures with reasonable width:
|
||||
If you are viewing the file in Emacs, eval the following to show the pictures with reasonable width:
|
||||
|
||||
```elisp
|
||||
(setq-local org-image-actual-width '(1024))
|
||||
|
|
|
|||
|
|
@ -793,7 +793,7 @@ bind <span style="color:#a6e22e">-M</span> insert <span style="color:#ae81ff">\e
|
|||
<span style="color:#66d9ef">else</span>
|
||||
notify-send <span style="color:#e6db74">"Terminal"</span> <span style="color:#e6db74">"Command execution complete"</span>
|
||||
<span style="color:#66d9ef">fi</span>
|
||||
</code></pre></div><h3 id="autocommmit"><code>autocommmit</code></h3>
|
||||
</code></pre></div><h3 id="autocommit"><code>autocommit</code></h3>
|
||||
<p>A script to autocommit files in a repository. I use it to sync my org directory and password store. I guess it’s not how git is intended to be used, but it works for me.</p>
|
||||
<p>Usage:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-text" data-lang="text">autocommit <repository> [-F]
|
||||
|
|
|
|||
|
|
@ -245,6 +245,15 @@
|
|||
<ul>
|
||||
<li><a href="#capture-templates-and-various-settings">Capture templates & various settings</a></li>
|
||||
<li><a href="#custom-agendas">Custom agendas</a></li>
|
||||
<li><a href="#org-ql">org-ql</a></li>
|
||||
<li><a href="#review-workflow">Review workflow</a>
|
||||
<ul>
|
||||
<li><a href="#data-from-git-and-org-roam">Data from git & org-roam</a></li>
|
||||
<li><a href="#data-from-org-journal">Data from org-journal</a></li>
|
||||
<li><a href="#data-from-org-agenda-via-org-ql">Data from org-agenda via org-ql</a></li>
|
||||
<li><a href="#capture-template">Capture template</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#org-journal">Org Journal</a></li>
|
||||
<li><a href="#org-roam">Org Roam</a>
|
||||
<ul>
|
||||
|
|
@ -425,7 +434,7 @@
|
|||
<li><a href="#elfeed">Elfeed</a>
|
||||
<ul>
|
||||
<li><a href="#some-additions">Some additions</a></li>
|
||||
<li><a href="#youtube">YouTube</a></li>
|
||||
<li><a href="#youtube-and-emms">YouTube & EMMS</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="#emms">EMMS</a>
|
||||
|
|
@ -2398,6 +2407,198 @@
|
|||
(<span style="color:#e6db74">"tp"</span> <span style="color:#e6db74">"Personal tasks"</span>
|
||||
((tags-todo <span style="color:#e6db74">"personal"</span>
|
||||
((org-agenda-prefix-format <span style="color:#e6db74">" %i %-12:c [%e] "</span>)))))))
|
||||
</code></pre></div><h4 id="org-ql">org-ql</h4>
|
||||
<p><a href="https://github.com/alphapapa/org-ql">org-ql</a> is a package to query the org files. I’m using it in my review workflow, perhaps later I’ll find another usecases.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(use-package org-ql
|
||||
:straight (:fetcher github
|
||||
:repo <span style="color:#e6db74">"alphapapa/org-ql"</span>
|
||||
:files (:defaults (:exclude <span style="color:#e6db74">"helm-org-ql.el"</span>))))
|
||||
</code></pre></div><h4 id="review-workflow">Review workflow</h4>
|
||||
<p>My take on a review workflow. As a baseline, I want to have a template that lists the important changes since the last review and other basic information. I’m doing reviews regularly, but the time intervals still may vary, hence this flexibility.</p>
|
||||
<h5 id="data-from-git-and-org-roam">Data from git & org-roam</h5>
|
||||
<p>First, as I have <a href="/configs/console/">autocommit</a> set up in my org directory, here is a handy function to get an alist of changed files of a form <code>(status . path)</code>. In principle, the <code>rev</code> parameter can be a commit, tag, etc but here I’m interested in a form like <code>@{2021-08-30}</code>.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(setq my/git-diff-status
|
||||
<span style="color:#f92672">'</span>((<span style="color:#e6db74">"A"</span> <span style="color:#f92672">.</span> added)
|
||||
(<span style="color:#e6db74">"C"</span> <span style="color:#f92672">.</span> copied)
|
||||
(<span style="color:#e6db74">"D"</span> <span style="color:#f92672">.</span> deleted)
|
||||
(<span style="color:#e6db74">"M"</span> <span style="color:#f92672">.</span> modified)
|
||||
(<span style="color:#e6db74">"R"</span> <span style="color:#f92672">.</span> renamed)
|
||||
(<span style="color:#e6db74">"T"</span> <span style="color:#f92672">.</span> type-changed)
|
||||
(<span style="color:#e6db74">"U"</span> <span style="color:#f92672">.</span> unmerged)))
|
||||
|
||||
(defun my/get-files-status (rev)
|
||||
(let ((files (shell-command-to-string (<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">"git diff --name-status "</span> rev))))
|
||||
(<span style="color:#a6e22e">mapcar</span>
|
||||
(lambda (file)
|
||||
(let ((elems (split-string file <span style="color:#e6db74">"\t"</span>)))
|
||||
(<span style="color:#a6e22e">cons</span>
|
||||
(<span style="color:#a6e22e">cdr</span> (<span style="color:#a6e22e">assoc</span> (<span style="color:#a6e22e">car</span> elems) my/git-diff-status))
|
||||
(<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> elems))))
|
||||
(split-string files <span style="color:#e6db74">"\n"</span> <span style="color:#66d9ef">t</span>))))
|
||||
</code></pre></div><p>I’ll use it to get a list of added and changed Roam files since the last review. Date should have be in a format <code>YYYY-MM-DD</code>.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(defun my/org-changed-files-since-date (date)
|
||||
(let ((default-directory org-directory))
|
||||
(my/get-files-status (<span style="color:#a6e22e">format</span> <span style="color:#e6db74">"@{%s}"</span> date))))
|
||||
</code></pre></div><p>Now we are ready to format this list to insert it into the capture template.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(defun my/org-review-format-roam (rev)
|
||||
(let* ((changes (my/org-changed-files-since-date rev))
|
||||
(new-roam
|
||||
(seq-filter
|
||||
(lambda (elem)
|
||||
(and (<span style="color:#a6e22e">eq</span> (<span style="color:#a6e22e">car</span> elem) <span style="color:#e6db74">'added</span>)
|
||||
(string-match-p (rx bos <span style="color:#e6db74">"roam"</span>) (<span style="color:#a6e22e">cdr</span> elem))))
|
||||
changes))
|
||||
(changed-roam
|
||||
(seq-filter
|
||||
(lambda (elem)
|
||||
(and (<span style="color:#a6e22e">eq</span> (<span style="color:#a6e22e">car</span> elem) <span style="color:#e6db74">'modified</span>)
|
||||
(string-match-p (rx bos <span style="color:#e6db74">"roam"</span>) (<span style="color:#a6e22e">cdr</span> elem))))
|
||||
changes)))
|
||||
(<span style="color:#a6e22e">concat</span>
|
||||
(unless (seq-empty-p new-roam)
|
||||
(<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">"** New Roam entries \n"</span>
|
||||
(<span style="color:#a6e22e">mapconcat</span>
|
||||
(lambda (entry)
|
||||
(<span style="color:#a6e22e">format</span> <span style="color:#e6db74">"- [[file:%s][%s]]"</span> (<span style="color:#a6e22e">cdr</span> entry) (<span style="color:#a6e22e">cdr</span> entry)))
|
||||
new-roam
|
||||
<span style="color:#e6db74">"\n"</span>)
|
||||
<span style="color:#e6db74">"\n"</span>))
|
||||
(unless (seq-empty-p changed-roam)
|
||||
(<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">"** Changed Roam entries \n"</span>
|
||||
(<span style="color:#a6e22e">mapconcat</span>
|
||||
(lambda (entry)
|
||||
(<span style="color:#a6e22e">format</span> <span style="color:#e6db74">"- [[file:%s][%s]]"</span> (<span style="color:#a6e22e">cdr</span> entry) (<span style="color:#a6e22e">cdr</span> entry)))
|
||||
changed-roam
|
||||
<span style="color:#e6db74">"\n"</span>))))))
|
||||
</code></pre></div><h5 id="data-from-org-journal">Data from org-journal</h5>
|
||||
<p>Second, I want to have a list of new jounal entries since the last review.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(defun my/org-journal-entries-since-date (rev-date)
|
||||
(<span style="color:#a6e22e">mapcar</span>
|
||||
(lambda (date)
|
||||
(let ((time (<span style="color:#a6e22e">encode-time</span> (parse-time-string date))))
|
||||
<span style="color:#f92672">`</span>((file <span style="color:#f92672">.</span> <span style="color:#f92672">,</span>(org-journal--get-entry-path time))
|
||||
(header <span style="color:#f92672">.</span> <span style="color:#f92672">,</span>(<span style="color:#a6e22e">format-time-string</span> org-journal-date-format time)))))
|
||||
(seq-filter
|
||||
(lambda (date) (<span style="color:#a6e22e">string-lessp</span> rev-date date))
|
||||
(<span style="color:#a6e22e">mapcar</span>
|
||||
(lambda (date)
|
||||
(<span style="color:#a6e22e">format</span> <span style="color:#e6db74">"%04d-%02d-%02dT00:00:00+0300"</span> (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">2</span> date) (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">0</span> date) (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> date)))
|
||||
(org-journal--list-dates)))))
|
||||
</code></pre></div><p>Format the results:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(defun my/org-review-format-journal (rev-date)
|
||||
(<span style="color:#a6e22e">mapconcat</span>
|
||||
(lambda (item)
|
||||
(<span style="color:#a6e22e">format</span> <span style="color:#e6db74">"- [[file:%s::*%s][%s]]"</span>
|
||||
(<span style="color:#a6e22e">cdr</span> (<span style="color:#a6e22e">assoc</span> <span style="color:#e6db74">'file</span> item))
|
||||
(<span style="color:#a6e22e">cdr</span> (<span style="color:#a6e22e">assoc</span> <span style="color:#e6db74">'header</span> item))
|
||||
(<span style="color:#a6e22e">cdr</span> (<span style="color:#a6e22e">assoc</span> <span style="color:#e6db74">'header</span> item))))
|
||||
(my/org-journal-entries-since-date rev-date)
|
||||
<span style="color:#e6db74">"\n"</span>))
|
||||
</code></pre></div><h5 id="data-from-org-agenda-via-org-ql">Data from org-agenda via org-ql</h5>
|
||||
<p>Third, I want to list some changes in my agenda. This section will change depending on what I’m currently working on.</p>
|
||||
<p>So, here is a list of queries results of which I want to see in the review template. The format is <code>(name date-field order-by-field query)</code>.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(setq my/org-ql-review-queries
|
||||
<span style="color:#f92672">`</span>((<span style="color:#e6db74">"Waitlist"</span> scheduled scheduled
|
||||
(and
|
||||
(done)
|
||||
(tags-inherited <span style="color:#e6db74">"waitlist"</span>)))
|
||||
(<span style="color:#e6db74">"Personal tasks done"</span> closed <span style="color:#f92672">,</span><span style="color:#66d9ef">nil</span>
|
||||
(and
|
||||
(tags-inherited <span style="color:#e6db74">"personal"</span>)
|
||||
(todo <span style="color:#e6db74">"DONE"</span>)))
|
||||
(<span style="color:#e6db74">"Attended meetings"</span> closed scheduled
|
||||
(and
|
||||
(tags <span style="color:#e6db74">"meeting"</span>)
|
||||
(todo <span style="color:#e6db74">"PASSED"</span>)))
|
||||
(<span style="color:#e6db74">"Done project tasks"</span> closed deadline
|
||||
(and
|
||||
(todo <span style="color:#e6db74">"DONE"</span>)
|
||||
(ancestors
|
||||
(heading <span style="color:#e6db74">"Tasks"</span>))))))
|
||||
</code></pre></div><p>The query will be executed like this: <code>(and (date-field :from rev-date) query)</code></p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(defun my/org-review-exec-ql (saved rev-date)
|
||||
(let ((query <span style="color:#f92672">`</span>(and
|
||||
(<span style="color:#f92672">,</span>(<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">1</span> saved) :from <span style="color:#f92672">,</span>rev-date)
|
||||
<span style="color:#f92672">,</span>(<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">3</span> saved))))
|
||||
(org-ql-query
|
||||
:select <span style="color:#a6e22e">#'</span>element
|
||||
:from (org-agenda-files)
|
||||
:where query
|
||||
:order-by (<span style="color:#a6e22e">nth</span> <span style="color:#ae81ff">2</span> saved))))
|
||||
</code></pre></div><p>Format one element of query result.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(defun my/org-review-format-element (elem)
|
||||
(<span style="color:#a6e22e">concat</span>
|
||||
(string-pad
|
||||
(<span style="color:#a6e22e">plist-get</span> (cadr elem) :raw-value)
|
||||
<span style="color:#ae81ff">40</span>)
|
||||
(when-let (scheduled (<span style="color:#a6e22e">plist-get</span> (cadr elem) :scheduled))
|
||||
(<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">" [SCHEDULED: "</span> (<span style="color:#a6e22e">plist-get</span> (cadr scheduled) :raw-value) <span style="color:#e6db74">"]"</span>))
|
||||
(when-let (deadline (<span style="color:#a6e22e">plist-get</span> (cadr elem) :deadline))
|
||||
(<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">" [DEADLINE: "</span> (<span style="color:#a6e22e">plist-get</span> (cadr deadline) :raw-value) <span style="color:#e6db74">"]"</span>))))
|
||||
</code></pre></div><p>Execute all the saved queries and format an Org list for the capture template.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(defun my/org-review-format-queries (rev-date)
|
||||
(<span style="color:#a6e22e">mapconcat</span>
|
||||
(lambda (results)
|
||||
(<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">"** "</span> (<span style="color:#a6e22e">car</span> results) <span style="color:#e6db74">"\n"</span>
|
||||
(string-join
|
||||
(<span style="color:#a6e22e">mapcar</span> (lambda (r) (<span style="color:#a6e22e">concat</span> <span style="color:#e6db74">"- "</span> r)) (<span style="color:#a6e22e">cdr</span> results))
|
||||
<span style="color:#e6db74">"\n"</span>)
|
||||
<span style="color:#e6db74">"\n"</span>))
|
||||
(seq-filter
|
||||
(lambda (result)
|
||||
(not (seq-empty-p (<span style="color:#a6e22e">cdr</span> result))))
|
||||
(<span style="color:#a6e22e">mapcar</span>
|
||||
(lambda (saved)
|
||||
(<span style="color:#a6e22e">cons</span>
|
||||
(<span style="color:#a6e22e">car</span> saved)
|
||||
(<span style="color:#a6e22e">mapcar</span>
|
||||
<span style="color:#a6e22e">#'</span>my/org-review-format-element
|
||||
(my/org-review-exec-ql saved rev-date))))
|
||||
my/org-ql-review-queries))
|
||||
<span style="color:#e6db74">"\n"</span>))
|
||||
</code></pre></div><h5 id="capture-template">Capture template</h5>
|
||||
<p>Now, we have to put all this together and define a capture template for the review.</p>
|
||||
<p>I’ll use a separate directory for the review files, just like for org-journal and org-roam. The filename will have a format <code>YYYY-MM-DD.org</code>, which will also free me from the effort of storing the last review date somewhere.</p>
|
||||
<p>If somehow there are no files in the folder, fallback to the current date minus one week.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(setq my/org-review-directory <span style="color:#e6db74">"review"</span>)
|
||||
|
||||
(defun my/org-review-get-filename ()
|
||||
(<span style="color:#a6e22e">concat</span> my/org-review-directory <span style="color:#e6db74">"/"</span> (<span style="color:#a6e22e">format-time-string</span> <span style="color:#e6db74">"%Y-%m-%d.org"</span> (<span style="color:#a6e22e">current-time</span>))))
|
||||
|
||||
(defun my/get-last-review-date ()
|
||||
(<span style="color:#a6e22e">substring</span>
|
||||
(or
|
||||
(-max-by
|
||||
<span style="color:#e6db74">'string-greaterp</span>
|
||||
(-filter
|
||||
(lambda (f) (not (or (<span style="color:#a6e22e">string-equal</span> f <span style="color:#e6db74">"."</span>) (<span style="color:#a6e22e">string-equal</span> f <span style="color:#e6db74">".."</span>))))
|
||||
(<span style="color:#a6e22e">directory-files</span> (f-join org-directory my/org-review-directory))))
|
||||
(<span style="color:#a6e22e">format-time-string</span>
|
||||
<span style="color:#e6db74">"%Y-%m-%d"</span>
|
||||
(<span style="color:#a6e22e">time-subtract</span>
|
||||
(<span style="color:#a6e22e">current-time</span>)
|
||||
(seconds-to-time (<span style="color:#a6e22e">*</span> <span style="color:#ae81ff">60</span> <span style="color:#ae81ff">60</span> <span style="color:#ae81ff">24</span> <span style="color:#ae81ff">7</span>)))))
|
||||
<span style="color:#ae81ff">0</span> <span style="color:#ae81ff">10</span>))
|
||||
</code></pre></div><p>A template looks like this:</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(setq my/org-review-capture-template
|
||||
<span style="color:#f92672">`</span>(<span style="color:#e6db74">"r"</span> <span style="color:#e6db74">"Review"</span> plain (file <span style="color:#f92672">,</span>(my/org-review-get-filename))
|
||||
<span style="color:#f92672">,</span>(string-join
|
||||
<span style="color:#f92672">'</span>(<span style="color:#e6db74">"#+TITLE: Review %t"</span>
|
||||
<span style="color:#e6db74">""</span>
|
||||
<span style="color:#e6db74">"Last review date: %(org-timestamp-translate (org-timestamp-from-string (format \"<%s>\" (my/get-last-review-date))))"</span>
|
||||
<span style="color:#e6db74">""</span>
|
||||
<span style="color:#e6db74">"* Roam"</span>
|
||||
<span style="color:#e6db74">"%(my/org-review-format-roam (my/get-last-review-date))"</span>
|
||||
<span style="color:#e6db74">"* Journal"</span>
|
||||
<span style="color:#e6db74">"New journal entries:"</span>
|
||||
<span style="color:#e6db74">"%(my/org-review-format-journal (my/get-last-review-date))"</span>
|
||||
<span style="color:#e6db74">"* Agenda"</span>
|
||||
<span style="color:#e6db74">"%(my/org-review-format-queries (my/get-last-review-date))"</span>
|
||||
<span style="color:#e6db74">"* Thoughts :crypt:"</span>
|
||||
<span style="color:#e6db74">"%?"</span>)
|
||||
<span style="color:#e6db74">"\n"</span>)))
|
||||
|
||||
(add-to-list <span style="color:#e6db74">'org-capture-templates</span> my/org-review-capture-template <span style="color:#66d9ef">t</span>)
|
||||
</code></pre></div><h4 id="org-journal">Org Journal</h4>
|
||||
<p><a href="https://github.com/bastibe/org-journal">org-journal</a> is a plugin for maintaining a journal in org mode. I want to have its entries separate from my knowledge base.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(use-package org-journal
|
||||
|
|
@ -3265,8 +3466,7 @@ MimeType=x-scheme-handler/org-protocol
|
|||
:config
|
||||
(add-hook <span style="color:#e6db74">'web-mode-hook</span> <span style="color:#e6db74">'smartparens-mode</span>)
|
||||
(add-hook <span style="color:#e6db74">'web-mode-hook</span> <span style="color:#e6db74">'hs-minor-mode</span>)
|
||||
(my/set-smartparens-indent <span style="color:#e6db74">'web-mode</span>)
|
||||
(add-hook <span style="color:#e6db74">'web-mode-hook</span> ))
|
||||
(my/set-smartparens-indent <span style="color:#e6db74">'web-mode</span>))
|
||||
</code></pre></div><p>Hooking this up with lsp.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(setq my/web-mode-lsp-extensions
|
||||
<span style="color:#f92672">`</span>(<span style="color:#f92672">,</span>(rx <span style="color:#e6db74">".svelte"</span> eos)
|
||||
|
|
@ -4208,7 +4408,7 @@ MimeType=x-scheme-handler/org-protocol
|
|||
(let ((link (elfeed-entry-link elfeed-show-entry)))
|
||||
(when link
|
||||
(eww link))))
|
||||
</code></pre></div><h5 id="youtube">YouTube</h5>
|
||||
</code></pre></div><h5 id="youtube-and-emms">YouTube & EMMS</h5>
|
||||
<p>Previously this block was opening MPV with <code>start-process</code>, but now I’ve managed to hook up MPV with EMMS. So there is the EMMS+elfeed “integration”.</p>
|
||||
<p>The following function converts URLs from Invidious and the like to YouTube.</p>
|
||||
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-emacs-lisp" data-lang="emacs-lisp">(defun my/get-youtube-url (link)
|
||||
|
|
@ -4226,12 +4426,13 @@ MimeType=x-scheme-handler/org-protocol
|
|||
(let ((track (emms-track
|
||||
<span style="color:#e6db74">'url</span> (my/get-youtube-url (elfeed-entry-link entry)))))
|
||||
(emms-track-set track <span style="color:#e6db74">'info-title</span> (elfeed-entry-title entry))
|
||||
(setq my/test track)
|
||||
(emms-playlist-insert-track track))))
|
||||
|
||||
(defun my/elfeed-add-emms-youtube ()
|
||||
(interactive)
|
||||
(emms-add-elfeed elfeed-show-entry))
|
||||
(emms-add-elfeed elfeed-show-entry)
|
||||
(elfeed-tag elfeed-show-entry <span style="color:#e6db74">'watched</span>)
|
||||
(elfeed-show-refresh))
|
||||
|
||||
(with-eval-after-load <span style="color:#e6db74">'elfeed</span>
|
||||
(general-define-key
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue