|
Gathering distance
----------------------------------------------------------------------
Even if your code doesn't work it's still a good idea to share the
progress you've made. That's what I told myself, anyways, as I set out
to start this article. At the time, my small modifications to
`ox-rss.el', a package that adds a RSS publishing backend to org-mode,
were broken. Well, not broken. But not working with my existing
org-publish project. I had the code working from a GUI Emacs
session. But when I published my website using the shell (`emacs -q
--batch --load publish-roygbyte_com.el --funcall org-publish-all'), it
"broke". So this article was to be a documentation of my progress and
a shy cry for help.
I tore myself away from the screen and enjoyed a little bit of "snack
and movement." Turns out, that was enough distance to let me gather
perspective on the problem at hand! Like I said, the code was working
from within a GUI Emacs session. The code, by the way, was a series of
functions that returned the first paragraph (or "excerpt") from an org
file. The entry point was `(org-file-first-paragraph id)', where `id'
is an org-roam id. It /just worked/ when testing in my GUI Emacs
session. But not in the shell--the function returned `nil' because it
couldn't find a file from the given id. Turns out, my publishing file,
`publish-roygbyte_com.el', had incorrectly set the location of my
org-roam directory. It couldn't find a file from the given id because
it was looking in the wrong place!
Anyways, to cut to the chase: I figured it out. Probably this article
should be more accurately called "Fully arrived at RSS article
excerpts with ox-rss.el". But I'm sentimental, and I want to keep the
initial context. Because: getting halfway is still distance
travelled. Next time I end up at a deadend, I'll want to reminder
/that/. Certainly I'm not the only programmer who gets physically
stuck in mental problems. But for my own growth, I'd like to get stuck
a /little/ less by taking breaks /more/.
Adding excerpts to ox-rss.el
----------------------------------------------------------------------
Below is the code at the time of writing. It's also available on my ,
where I'll post updates and bug fixes.
rss.org
......................................................................
The apptly named property `:EXCERPT_FROM_ID:' is passed an id to an
org file. The id doesn't have to be the same as the file referenced in
the `:RSS_PERMALINK': property. Imagine you wanted to have the RSS
entry like to some random article on the internet, and you wanted the
excerpt to be your thoughts on the matter.
,----
| * On comics and code
| :PROPERTIES:
| :PUBDATE: <2022-08-23 Tue>
| :EXCERPT_FROM_ID: e7d501c8-91af-4811-8e24-ec7fe614d3b5
| :RSS_PERMALINK: comics_and_code.html
| :END:
`----
org-rss-headline / ox-rss.el
......................................................................
A small modification to `org-rss-headline's' function: add a
conditional assignment to the contents variable. If the
`:EXCERPT_FROM_ID:' property is set in the headline, and the property
is a string, `org-file-first-paragraph' will return the excerpt from
the given org file. Otherwise, contents remains unchanged.
,----
| ;; Excerpt from ox-rss.el
| (defun org-rss-headline (headline contents info)
| "Transcode HEADLINE element into RSS format.
| CONTENTS is the headline contents. INFO is a plist used as a
| communication channel."
| (unless (or (org-element-property :footnote-section-p headline)
| (> (org-export-get-relative-level headline info) 1))
| (let* ((author (and (plist-get info :with-author)
| (let ((auth (plist-get info :author)))
| (and auth (org-export-data auth info)))))
| ;; Roygbyte's excerpt code.
| (contents (let ((id (org-element-property :EXCERPT_FROM_ID headline)))
| (if (stringp id)
| (org-file-first-paragraph id)
| contents)))
| ;; Excerpt ends ...
`----
org-file-first-pragraph (and other functions) / ox-rss.el
......................................................................
The rest of the code is contained below. Rather than explain what's
going on (the functions include documentations which should be enough
te get a grip on what's going on), I'll explain how I got it to work!
The recipe, if you will, for hacking Lisp and org-mode. Here's what
kept me afloat:
- `C-h f', for learning what functions existed, and how they worked.
- `M-x' shortdoc-display-group, for learning about buffers.
- `*scratch*', for testing code.
- `ox-publish.el' and `org-element.el', for learning about parsing org
files.
- /ANSI Common Lisp/ by Paul Graham, for learning about cons, car, cdr,
and lists.
- DuckDuckGo, obviously.
,----
|
| (defun file-contents (filename)
| "Given a filename, return the corresponding file's contents."
| (with-temp-buffer
| (insert-file-contents filename)
| (buffer-string)))
|
| (defun org-file-to-element-tree (file)
| "Given the path to an org file, return the file's element tree"
| (with-temp-buffer
| (insert-file-contents file)
| (org-element-parse-buffer 'greater-element)))
|
| (defun extract-region-from-file (filename position)
| "Given a path to a file, extract the content between the start and end position"
| (with-temp-buffer
| (insert-file-contents filename)
| (let ((region (buffer-substring (car position) (cdr position))))
| (with-temp-buffer
| (insert region)
| (buffer-string)))))
|
| (defun org-tree-to-paragraph-positions (org-file-tree)
| "Given an org-file tree, get the first paragraph positions. Returns a cons."
| (org-element-map org-file-tree
| 'paragraph (lambda (p)
| (cons (org-element-property :begin p)
| (org-element-property :end p))
| )))
|
| (defun org-roam-id-to-file-path (id)
| "Given an org roam id, return the file's path"
| (let ((file-path (org-roam-id-find id)))
| (if (arrayp file-path)
| (file-truename (car (org-roam-id-find id)))
| nil)))
|
| (defun org-file-first-paragraph (id)
| "Given an org roam file by ID, find the first paragraph of the file"
| (if (stringp id)
| (let ((file-path (org-roam-id-to-file-path id)))
| (if (stringp file-path)
| (let ((file-contents (file-contents file-path))
| (file-tree (org-file-to-element-tree file-path)))
| (extract-region-from-file file-path (nth 0 (org-tree-to-paragraph-positions file-tree))))
| nil
| ))
| nil))
|
`----
publish.el
......................................................................
,----
| (require 'org-roam)
| (require 'ox-rss)
| (use-package org-roam
Happy helping ☃ here: You tried to output a spurious TAB character. This will break gopher. Please review your scripts. Have a nice day!
Happy helping ☃ here: You tried to output a spurious TAB character. This will break gopher. Please review your scripts. Have a nice day!
Happy helping ☃ here: You tried to output a spurious TAB character. This will break gopher. Please review your scripts. Have a nice day!
Happy helping ☃ here: You tried to output a spurious TAB character. This will break gopher. Please review your scripts. Have a nice day!
Happy helping ☃ here: You tried to output a spurious TAB character. This will break gopher. Please review your scripts. Have a nice day!
Happy helping ☃ here: You tried to output a spurious TAB character. This will break gopher. Please review your scripts. Have a nice day!
Happy helping ☃ here: You tried to output a spurious TAB character. This will break gopher. Please review your scripts. Have a nice day!
Happy helping ☃ here: You tried to output a spurious TAB character. This will break gopher. Please review your scripts. Have a nice day!
| (setq org-id-extra-files (org-roam-list-files))
`----
Final thoughts
----------------------------------------------------------------------
Lisp is a beautifully simple language, although it may not seem that
way at first glance. I'm grateful I've been able to muster the
patience to learn Lisp. Already, even with a very noviced
understanding of its principles, I'm able to put it to use. |