Categories: geek » emacs » org

RSS - Atom - Subscribe via email

Collect my recent toots in an Org file so that I can refile them

| emacs, mastodon, org

I want to use my microblog posts on Mastodon as building blocks for longer posts on my blog. Getting them into an Org file makes it easier to link to them or refile them to other parts of my Org files so that I can build up my notes.

(use-package pandoc)
(defun my-mastodon-org-feed-formatter (entry)
  (concat "* " (pandoc-convert-stdio
                (dom-text (dom-by-tag
                           (with-temp-buffer
                             (insert "<item>"
                                     (plist-get entry :item-full-text)
                                     "</item>")
                             (xml-parse-region (point-min) (point-max)))
                           'description))
                "html" "org")
          "\n\n[" (format-time-string (cdr org-time-stamp-formats)
                                      (date-to-time (plist-get entry :pubDate)))
"]\n" (plist-get entry :link)))
(setq org-feed-alist '(("Mastodon" "https://emacs.ch/@sachac/with_replies.rss"
                        "~/sync/orgzly/toots.org" "Toots"
                        :formatter my-mastodon-org-feed-formatter)))
(defun my-org-feed-sort (pos entries)
  (save-excursion
    (goto-char pos)
    (when (looking-at org-complex-heading-regexp)
      (org-sort-entries nil ?T))))
(advice-add #'org-feed-add-items :after #'my-org-feed-sort)

Now I can use org-feed-update-all (C-c C-x g) to pull things into my toots.org file.

This is part of my Emacs configuration.

Logging sent messages to Org Mode with message-sent-hook

| org, emacs

I wanted to e-mail all the EmacsConf speakers who had already uploaded their videos, and I wanted to keep track of the fact that I'd mailed them by adding a note to the :LOGBOOK: drawer in their talk heading. That way, organizers can just look at the logbook to see if we've mailed someone instead of digging through our mailboxes.

org-store-log-note assumes that it's called from the log buffer created by org-add-log-note. It doesn't seem to have a smaller function that can be called to store notes non-interactively, but that's okay. We can just set up the correct markers and call it from a temporary buffer.

(defun emacsconf-add-to-logbook (note)
  "Add NOTE as a logbook entry for the current subtree."
  (move-marker org-log-note-return-to (point))
  (move-marker org-log-note-marker (point))
  (with-temp-buffer
    (insert note)
    (let ((org-log-note-purpose 'note))
      (org-store-log-note))))

Then it's convenient to have a function that adds a note to a specified talk:

(defun emacsconf-add-to-talk-logbook (talk note)
  "Add NOTE as a logbook entry for TALK."
  (interactive (list (emacsconf-complete-talk) (read-string "Note: ")))
  (save-excursion
    (emacsconf-with-talk-heading talk
      (emacsconf-add-to-logbook note))))

I discard many drafts on the way to finalizing the process, so I want the note to be stored only after I actually send the mail. That's the job of message-sent-hook. My mail merge function calls compose-mail, sets up the body of the buffer, and then adds a lambda function to message-sent-hook to file the note in the logbook when sent.

(add-hook 'message-sent-hook
          `(lambda ()
             (mapc
              (lambda (o)
                (emacsconf-add-to-talk-logbook o "Sent speaker-after-video email"))
              (list ,@(mapcar (lambda (talk) (plist-get talk :slug)) talks))))
          nil t)

To see the mail merge code in context, you can check out the TODO entry at https://emacsconf.org/2022/organizers-notebook/#speaker-after-video . It uses functions from emacsconf.el and emacsconf-mail.el at https://git.emacsconf.org/emacsconf-el/ .

Why I Love Emacs - from Bob Oliver

| emacs, org

Sometimes I post updates from people who don't have their own blog. Here's one from Bob Oliver. - Sacha

This short article sets out why I, as an Emacs newbie, really, really love this software. But before I get into that I would like to explain my voyage (Note: absence of the 'journey' word) to Emacs.

Many moons ago, back in the late seventies / early eighties I was a Cobol programmer, a job I loved. As it is with life, circumstances change and I moved away from Data Processing, as we called it in olden days. This meant I had to get my programming fix using my Sinclair Spectrum, which I programmed using their version of BASIC. I learned how to build my own, very simple games, and spent many hours playing my games and programming more. Then the children came along, the Sinclair went into the loft (attic for non-UK readers) and I had little or no time for hobbies.

Years later, with family grown and flown the nest, the Raspberry Pi was released and revised my love of programming. I took to learning C and Python - though remain very much at the beginner stage. All very enjoyable. This sparked a notion that I might be able to build an app and enhance my future pension prospects. To this end I installed xCode on my MacBook and also tried VS-Code. Needless to say I have not achieved proficiency and have since removed those products from my MacBook.

I still wanted to enhance my knowledge of C, Python and Bash, and so was really pleased when the Raspberry Pi foundation released Raspberry O/S Desktop for Mac (apologies if this name is not technically correct). This enabled me to re-purpose an old MacBook (circa 2009 and no longer supported) as a Linux machine, which got me interesting in learning all things Linux. This led to me installing Emacs as my code editor. Through reading all things Emacs I discovered org-mode and now Emacs is my text editor of choice.

As probably most new users to Emacs, I found it a bit confusing at first, but did as recommended stuck with it, and I am really glad I did.

What do I use Emacs for?

A very good question. Short answer is code and text editor.

  1. Writing, compiling, testing and running C programs.
  2. Writing, testing and running Bash scripts.
  3. Writing, testing and running Python programs.
  4. Compiling my, not so, daily journal.
  5. Using org-mode as my word processor of choice.

The key reason for using org-mode for my journal, was portability and long term accessibility. I had used various electronic journals before, each with their own proprietary file standards, making me concerned that my journal would not be available to my children long after I have gone. Also as Linux, and hence org-mode, use plain text files I can edit with any text editor on any platform, so can be assured that I can move the files as and when I change computers. Also as plain text files, they are readily searchable, so I can recall memories easily.

Finding Emacs and org-mode is probably one of the best things I have done since I retired from full-time employment.

What next:

  1. Maintain my journal writing.
  2. Write up my poems in org-mode - I have several going back to my teenage years.
  3. Develop my writing skills and maybe write a novel.
  4. Learn how to send and recieve mail through Emacs - I have yet to find a guide that is not too technical / complicated for me.

SO MY MESSAGE IS JOIN THE EMACS AND ORG SOCIETY - YOU WON'T REGRET IT.

Bob Oliver Essex, England.

Add a note to the bottom of blog posts exported from my config file

Posted: - Modified: | emacs, org

Update: 2021-04-18: Tweaked the code so that I could add it to the main org-export-filter-body-functions list now that I'm using Eleventy and ox-11ty.el instead of Wordpress and org2blog.

I occasionally post snippets from my Emacs configuration file, drafting the notes directly in my literate config and posting them via org2blog. I figured it might be a good idea to include a link to my config at the end of the posts, but I didn't want to scatter redundant links in my config file itself. Wouldn't it be cool if the link could be automatically added whenever I use org2blog to post a subtree from my config file? I think the code below accomplishes that.

(defun my/org-export-filter-body-add-emacs-configuration-link (string backend info)
  (when (and (plist-get info :input-file) (string-match "\\.emacs\\.d/Sacha\\.org" (plist-get info :input-file)))
    (concat string
            (let ((id (org-entry-get-with-inheritance "CUSTOM_ID")))
              (format
               "\n<div class=\"note\">This is part of my <a href=\"https://sachachua.com/dotemacs%s\">Emacs configuration.</a></div>"
               (if id (concat "#" id) ""))))))

(use-package org
  :config
  (add-to-list 'org-export-filter-body-functions #'my/org-export-filter-body-add-emacs-configuration-link))
This is part of my Emacs configuration.

Org Mode: Create a quick timestamped note and capture a screenshot

Posted: - Modified: | emacs, org

I wanted to be able to quickly create timestamped notes and possibly capture a screenshot. Prompting for a value inside an org-capture-template disrupts my screen a little, so maybe this will make it as easy as possible. I could probably do this without going through org-capture-templates, but I wanted to take advantage of the fact that Org Mode will deal with the date tree and finding the right position itself.

(use-package org
  :config
  (add-to-list 'org-capture-templates
               '("p" "Podcast log - timestamped" item
                 (file+olp+datetree "~/orgzly/timestamped.org")
                 "%<%H:%M:%S,%3N> %^{Note}"
                 :immediate-finish t)))
  (defun my/org-capture-prefill-template (template &rest values)
    "Pre-fill TEMPLATE with VALUES."
    (setq template (or template (org-capture-get :template)))
    (with-temp-buffer
      (insert template)
      (goto-char (point-min))
      (while (re-search-forward
              (concat "%\\("
                      "\\[\\(.+\\)\\]\\|"
                      "<\\([^>\n]+\\)>\\|"
                      "\\([tTuUaliAcxkKInfF]\\)\\|"
                      "\\(:[-a-zA-Z]+\\)\\|"
                      "\\^\\({\\([^}]*\\)}\\)"
                      "?\\([gGtTuUCLp]\\)?\\|"
                      "%\\\\\\([1-9][0-9]*\\)"
                      "\\)") nil t)
        (if (car values)
            (replace-match (car values) nil t))
        (setq values (cdr values)))
      (buffer-string)))
(defun my/capture-screenshot (time &optional note)
  "Capture screenshot and save it to a file labeled with TIME and NOTE.
Return the filename."
  (interactive (list (current-time) (read-string "Note: ")))
  (let* ((filename (expand-file-name
                        (concat "Screenshot_"
                                (format-time-string "%Y%0m%d_%H%M%S" time)
                                (if note (concat " " note) "")
                                ".png")
                        "~/Pictures"))
         (cmd (concat "spectacle -b -o "
                      (shell-quote-argument filename))))
    (shell-command cmd)
    filename))
(defun my/capture-timestamped-note (time note)
  "Disable Helm and capture a quick timestamped note."
  (interactive (list (current-time) (read-string "Note: ")))
  (let ((helm-completing-read-handlers-alist '((org-capture . nil)))
        (entry (org-capture-select-template "p")))
    (org-capture-set-plist entry)
    (org-capture-get-template)
    (org-capture-set-target-location)
    (org-capture-put
     :template (org-capture-fill-template
                (my/org-capture-prefill-template (org-capture-get :template)
                                                 (format-time-string "%H:%M:%S,%3N")
                                                 note)))
    (org-capture-place-template)
    (org-capture-finalize)))
(defun my/capture-timestamped-note-with-screenshot (time note)
  (interactive (list (current-time) (read-string "Note: ")))
  (kill-new (my/capture-screenshot time note))
  (my/capture-timestamped-note time note))

Then I can call it with h h n for my/capture-timestamped-note or h h i for my/capture-timestamped-note-with-screenshot via keyboard shortcuts defined elsewhere in my config (see my/key-chord-commands).

#org-mode answers: task creation time, subtree at end, Emacs Lisp variables in TBLFM, logbook and refile

Posted: - Modified: | emacs, org

In the interest of getting more tips out there so that they can be searchable, here are a few things I helped people out with on the #org-mode channel on freenode.net and through e-mail.

How can I log task creation times in Org Mode?

You can use an Org capture template.

How can I create a subtree at the end of the current entry?

C-u C-u C-RET M-right gets you the behaviour without configuration, or you can use:

(defun my/org-insert-subheading-after () (interactive) (org-insert-subheading '(16)))

and bind it to a speed command or a shortcut.

How can I refer to Emacs Lisp variables in #+TBLFM?

#+TBLFM: @1$2='(+ @1$1 my-var1);L

How can I write a command that adds a logbook entry and refiles a subtree?

Here was the source that someone asked me for help on:

#+TODO: TODO(t!) | DONE(d!)
#+NAME: startup
#+BEGIN_SRC emacs-lisp
(setq org-log-into-drawer t)
(setq org-use-speed-commands t)
(defun my/refiletree (file headline &optional arg)
               (let ((pos (save-excursion
                      (find-file file)
                      (org-find-exact-headline-in-buffer headline))))
               (org-refile arg nil (list headline file nil pos)))
               (switch-to-buffer (current-buffer)))

;;(setq org-use-speed-commands 'my/org-use-speed-commands-for-headings-and-lists)

(add-to-list 'org-speed-commands-user '("t" (lambda ()
                                               (org-todo "TODO")
                                               (my/refiletree buffer-file-name "Next"))))
(add-to-list 'org-speed-commands-user '("d" (lambda ()
                                               (org-todo "DONE")
                                               (my/refiletree buffer-file-name "Done"))))
#+END_SRC

* Inbox
** Task 1
** Task 2
** Task 3
* Next
* Done

The problem was that the logbook entry was getting added to the wrong heading, since the subtree had already been refiled. It's because logging is done in post-command-hook (example code from org-add-log-setup: (add-hook 'post-command-hook 'org-add-log-note 'append)). That's why it gets confused. Try this. It defines a function to add to org-after-refile-insert-hook.

(setq org-log-into-drawer t)
(setq org-use-speed-commands t)
(defmacro my/def-state-and-refile-shortcut (key state heading)
  `(progn
     (defun ,(intern (concat "my/change-state-to-" state)) ()
       (org-todo ,state)
       (remove-hook 'org-after-refile-insert-hook (quote ,(intern (concat "my/change-state-to-" state)))))
     (add-to-list 'org-speed-commands-user
                  '(,key
                    (lambda ()
                      (add-hook 'org-after-refile-insert-hook (quote ,(intern (concat "my/change-state-to-" state))))
                      (my/refiletree buffer-file-name ,heading))))))
(my/def-state-and-refile-shortcut "t" "TODO" "Next")
(my/def-state-and-refile-shortcut "d" "DONE" "Done")
(defun my/refiletree (file headline &optional arg)
  (let ((pos (with-current-buffer (or (find-buffer-visiting file)
                                      (find-file-noselect file))
               (save-excursion
                 (org-find-exact-headline-in-buffer headline)))))
    (org-refile nil nil (list headline file nil pos))))

2020-09-07 Emacs news

Posted: - Modified: | emacs, org

Almost forgot to say: EmacsConf 2020 Call for Proposals is open until Sept 30, 2020. Please encourage someone you'd like to hear from! =)

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Hacker News, planet.emacslife.com, YouTube, the Emacs NEWS file and emacs-devel.