0

I'm attending a conference, and I have downloaded all abstracts so I can read them offline. They are all html files with the same structure. The files are numbered according to abstract submission number, so 0001.html, 0002.html, 0003.html etc. If I run a find-file, it doesn't help me much in finding abstracts by name.

What I would like to do is for find-file to not prompt me for filenames, but for the title that is within the html file. When I select a title, Emacs opens the html file with eww-browse-url. Here's what I have so far:

I can extract the titles with:

(defun read-html-into-dom (html)
  (with-temp-buffer
    (insert-file-contents html)
    (libxml-parse-html-region (point-min) (point-max))))

(defun abstract-extract-title (html)
  (let ((title (car (dom-by-class (read-html-into-dom html) "submissionTitle"))))
    (if title
        (dom-text title)
      "No title")))

and I can open the abstracts by filename with:

(defun conference--find-abstract-file ()
  (interactive)
  (let ((default-directory conference-dir))
    (concat "file://"
            (expand-file-name (read-file-name "Choose abstract:")))))

(defun conference-find-abstract ()
  (interactive)
  (eww-browse-url (call-interactively #'conference--find-abstract-file)))

How could I combine these two, such that when I call conference-find-abstract it runs over all filenames and shows me the titles? I know I have to change conference--find-abstract-file, but I'm lost at how to do this transformation, since eww-browse-url needs filenames.

Any examples of similar problems and solutions, or names of functions that could help me would be welcome. I suppose I'm looking for a common pattern, but don't know what it is called.

2 Answers 2

2

I would use an alist: ( (title . filename) ...). You can construct it to begin with something like this:

  (defun construct-abstract-file-alist ()
    (let ((files (directory-files conference-dir t ".*\\.html$")))
      (-zip (mapcar #'abstract-extract-title files) files)))

  (setq conf-abstract-file-alist (construct-abstract-file-alist))

This uses -zip from the dash library (available in ELPA).

Then you can use something like this to get the file name from the title:

(defun abstract-file-name (title)
   (cdr (assoc title conf-abstract-file-alist)))

That's just a POC of course: you most probably want the alist presented in a dired-like buffer to allow you to point and click (or navigate and RET). But the important thing is the data structure: the rest is icing on the cake and left as an exercise for the interested reader.

NB Error checking is non-existent and if your files are ill-formatted, you might end up with several entries in the alist with key "No title". Resolving that is also left as an exercise for the interested reader :-)

1
  • 1
    This is exactly what I was looking for. Including the exercises! Thank you
    – Daniel
    May 4, 2022 at 4:34
1

Based on NickD's answer, the complete minimal working solution:

(defun construct-abstract-file-alist ()
    (let ((files (directory-files conference-dir t ".*\\.html$")))
      (-zip (mapcar #'abstract-extract-title files) files)))

(defun abstract-file-name (title)
   (cdr (assoc title conf-abstract-file-alist)))

(setq conf-abstract-file-alist (construct-abstract-file-alist))

;; Interactive find by title
(defun conference-open-abstract (title)
  (interactive 
   (list (completing-read "Choose abstract: " conf-abstract-file-alist)))
  (let ((file (abstract-file-name title)))
    (if file
        (eww-browse-url (concat "file://" file))
      (message "No abstract for %s" title))))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.