Emacs org-mode

Welcome to the Emacs org-mode category.

You can subscribe to this category only via RSS!

I am working on restoring some of my related posts from my old blog, so keep checking here for new content.

Auto-generated description: A mythical creature resembling a satyr with glasses is sitting cross-legged in a forest setting, holding a glowing orb in front of a laptop.

    Still working with Claude on my Emacs settings as a reference. Need to start a new session, so asked it to write a summary with instructions on how I like it to help me. It wrote this:

    I know it’s complementing me on purpose, but that emphasized why? That’s why my blog is called this way 😉

    Say you go jump to a header in org-mode somewhere with C-c C-o. Did you know that you can go back with C-c &? And that it has history, so you can keep going back??

    Years of isearch-backwards… wasted. Mind blown. 🤯

    A Quick org-mode Internal Links Reference

    As I mentioned yesterday, I started organizing (and cleaning) my Emacs config. What may have seemed like an annoying and boring task has proven to be fun and addicting (reminds me of this, which is part of this video 😂).

    I’ve already discovered a couple of new improvements to my current config as I was researching individual settings and why I have them, and as I said I do plan to share it. For now though, one important section I added to my config is a quick org-mode links reference. This is actually the first time I figured out the difference between radio links and “regular” plain links and how to use them.

    Here it is, slightly modified:

    Org-ID

    We know about links to other files using org-id: You give a header an :ID: property (this is generated automatically1 when you link to a header) and you get a slug, usually a UUID (for me it’s something slightly else, more on that soon below). This stores the links in org-id-locations, which in turn points to a local file with all of these unique IDs. This is good for linking between headers in different files.

    Custom-ID

    We can set a :CUSTOM_ID: property for a header, and it’s only active in that file. Kind of like a table of contents in a book, with chapters (= headers) listed. If you don’t have the book, you don’t have its table of contents, so you know nothing about it. To point to a header that has a custom ID: [[#custom-id-value][description]].

    These are buffer-specific only (no cross-reference between files). In this case, the links are tied to words (anchors), not headers. There are two kinds.

    The regular plain target link works when we set the anchor first with <<an anchor>>, then call it again with [[an anchor]].

    Its more “magical” brother is a radio target link, where we set an anchor like so <<<very special anchor>>> (three angle brackets) and then all we need to do is type very special anchor (no brackets this time), and it works. Note though that when you create a radio target for the first time, you have to revert the buffer or M-x org-update-radio-target-regexp for it to take effect.

    Footnotes

    1 : this can be done with a function, org-id-get-create. For a deeper dive into how and why I set my org-id with a date format (not UUID), look here.

    Thought my Emacs init file needs a bit of cleaning - it contains configs from maybe 5 and 6 years ago. It’s a nostalgic learning experience. More fun than I thought. More to come soon 🤓

    Meta Journal Notes in denote-journal with Journelly

    Wow, I’m so good at overcomplicating things, I already made you go “huh?!” with the title alone! I have some mad skillz!

    On with the show:

    In the last couple of weeks, I slowly improved and tinkered with my Linux environment (Kubuntu). I’m not sure if it was a single thing that nudged me to do that, but between installing the latest version of Harper and switching to Vivaldi with its great features I keep discovering, I also started using denote-journal.

    Denote-journal, from the prolific Prot who also made Denote, was something I wanted to try for a while. I didn’t really have a good reason to, because I’ve been using Journelly for a long time, and on my Mac or Linux desktop, I’d call a capture template that would append to the file, adding my additional entries.

    But I always had one major issue with journaling on my iPhone: privacy. My iPhone is owned and regulated (with a system profile) by my workplace. So even if I’m fine writing personal notes on my Mac (I’m not, I’m iffy about it as well, I don’t trust Apple to respect my privacy much more than I trust Google), at the end of the day, these notes also sync to my iPhone.

    On a day-to-day basis, for quick thoughts and work notes, Journelly and my iPhone are great, but when it comes to writing longer notes about my future plans, how I spent the weekend with my partners, and basically anything else that involves people whose privacy I respect, I always self-censor.

    For this reason, I came up with a way to create private notes with Denote only on Linux. These notes only live on my Linux Desktop, with a few of those syncing to my Android1 (again, not great, but at least it’s not work-managed).

    I always feel more like myself on Linux because I am more myself - whether it’s customizing my shortcuts and workflow exactly how I like it, or if it’s the built-in privacy that can be further fortified and inspected. Journaling on Linux, without the world’s biggest nanny peeping over my shoulder, is where I really open up and write my most personal thoughts.

    A couple of times I looked at my Journelly notes, those that I wrote on the Mac where I could write at length and use the full power of Emacs, and compared them to my older journal on Linux. Well, there is no comparison. And while creating a private note in Denote in Linux and linking it back to the original works2, it introduces friction that hinders the flow of my thoughts.

    So I looked into denote-journal, realized it’s very easy to use, and gave it a try one day, and since then:

    Auto-generated description: A list of journal files, organized by date and day of the week, is shown in a directory format.

    Essentially, it’s what I used to do when I started using Journelly: refile my Journelly headers into my journal files when I get the chance. Or, as a matter of fact, I use org-refile-copy, which does exactly what it says, because I want to keep the original in Journelly. Since I now use individual journal files instead of a big file split into weeks (as I did in the past), this process is even easier. The only catch is the images.

    Images in Journelly are saved in its /Journelly.org.assets folder, where my journal files can’t see them directly; and even if it did, these images are too big and cause freezes, and also oriented the wrong way, so they need at least a minimal treatment. For these reasons, I attach each image to the correct header in the journal daily file which I moved over from Journelly.

    These journal notes are fantastic. I have my quick notes from the day available, but when I want to extend, all I do now is just write a new entry for that day (which looks just like the Journelly entries) and write to my heart’s content. There’s also a bonus: denote-journal allows me to make up for days if I didn’t create a “meta journal note” for the day (so far this happened only once) directly from the calendar, so if I miss a day, I go to the calendar, point at the missing day, and use denote-journal-new-or-existing-entry to take care of things. If I already have an entry, it jumps to it; if I don’t, it creates one.

    Between my journal on Linux, the emails I write to other bloggers, my blog posts, and the occasional instructions I write in Denote, I think I write more than I did in my entire life. I’m thinking I need to start capturing it in some sort of book, though I have no idea what it will be about and how to edit my writings in a way that makes sense. This is only a vague concept at the moment.

    Footnotes

    1 : I keep skirting around this issue so I’ll just mention it quickly: I used GrapheneOS in the past, and it’s great for these kinds of things, but GrapheneOS protects your phone to an extent that certain apps don’t work.

    2 : I’ve been doing this often enough that I have a whole tag in Denote called “supplemental” with additional thoughts and notes. In Journelly, I was just linking to those, and writing something like “I have more to say about this” and this would include a Denote link to the Linux-only note.

    Sometimes you just gotta laugh at yourself.

    Interview with an Emacs Enthusiast in 2023 with Emerald McS., PhD

    People don’t quit Emacs. They just die at some point.

    Brilliant.

    Correcting photo orientation for org-mode in Linux

    Here’s a niche problem: When viewing iPhone-captured photos in org-mode on Linux, they always appear in landscape orientation, even if you took them in portrait.

    The reason is that the sensor of the camera on the phone is physically embedded in landscape mode, so all photos are in landscape mode; when you hold the phone in portrait mode (which is how you hold it most of the time), the phone detects that and implements a fix in the EXIF data file. Essentially, a software fix to a hardware design issue.

    Since most photos I take are usually in portrait orientation, it means I need to twist my neck and view images at a 90-degree angle when I look at my Journelly entries.

    The source of the problem seems to stem from how org-mode interprets EXIF data in the photo: it doesn’t. It relies on other parts of Emacs, which in turn rely on parts of the OS, to do the job. On Linux (at least on Kubuntu, which is what I use these days), those parts don’t handle EXIF orientation information. Why and how, I am not sure, it’s more digging than I have the time for right now… but anyway - there’s a simple fix.

    Auto-orient, which is part of ImageMagick’s mogrify tool. And if you use Emacs on Linux, good chance it’s already installed:

    This operator reads and resets the EXIF image profile setting ‘Orientation’ and then performs the appropriate 90 degree rotation on the image to orient the image, for correct viewing.

    To execute on an image file: mogrify -auto-orient <file>.

    And because I use Emacs, of course there’s a dwim-shell-command solution:

        (defun jtr/dwim-image-auto-orient ()
          "Auto-orient images based on EXIF data using mogrify."
          (interactive)
          (dwim-shell-command-on-marked-files
           "Auto-orient images"
           "mogrify -auto-orient '<<f>>'"
           :utils "mogrify"
           :silent-success t))
    

    That last part,:silent-success, closes the empty buffer that pops up after successful execution, as mogrify doesn’t really produce an output window. So, it will just bring us back to Dired.

    Installing Harper on Kubuntu: The Right Way. Maybe.

    I recently installed Harper on my Linux Desktop to work with Emacs, but since I’m running Kubuntu, I ran into difficulties. In short, there’s no Flatpak or Apt option when it comes to Harper.

    After a few interesting changes to the way I journal in Emacs (this is something I hope to discuss soon), I decided to go for the full version, and that meant installing Rust and Cargo.

    This was another “programming quest” I didn’t know how to start in the past. I used Claude.ai to guide me, but as usual, I asked a million questions about everything, so I can explain it again here (this is my test to myself). So if you’re new to all of this like I am, take the explanations with a grain of salt, and if you’re an experienced developer who understands Rust (and curl, for that matter) feel free to reach out and educate me further.

    Alright, here we go:

    To install Rust and Cargo with it:

        curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
    

    The above is a bit more complex than what’s in Cargo’s documentation, but based on a quick search, it is what’s directly recommended in rustup, which is where you install Rust. The idea is the same as other curl installations, with a few more options for added security and to ensure we’re getting what we really want:

    Run curl, but restrict it only to https (no http):

    curl --proto '=https'

    Somewhat redundant: curl will usually refuse anything lower than 1.2 by default. This forces TLS 1.2 as the minimum. This is good practice and also what they tell us to use, so why not:

    --tlsv1.2

    options for silent mode s (so don’t show us progress and status), but show us if we get errors S, and if we get a 404 error or similar, just stop silently f (otherwise it will pipe it into the sh command at the end):

    -sSf

    Then we have the URL to download from:

    https://sh.rustup.rs

    and finally we pipe it | into a shell sh command so it runs as a script as intended here. If you go to the above URL directly, it will download a shell script - so this is how we get it and run it in one go:

    | sh

    Because we’re about to run commands for Rust, it’s a good idea to add it to our source environment, the same as editing ~/.bashrc manually and adding . "$HOME/.cargo/env". Without it, we’ll have to specify where Cargo is installed for the next commands

        source ~/.bashrc
    

    At first, I installed what was available on crates.io. Crates, as I learned, is the official repository for Cargo, our “app store” for Rust, (or Elpa for Emacs). The individual packages are called “crates”. Makes sense now, but before it all looked like a bunch of command-line voodoo to me.

    However, apperently what’s available on Crates is not up to snuff. The official repository for Harper is at https://github.com/Automattic/harper, and it specifies version 2.3.1, whereas the one available in Crates is 2.0.0. We are still using cargo (it’s the “installer” for Rust), but specify to get what we need directly from there:

        cargo install --git https://github.com/Automattic/harper harper-ls --locked
    

    The git option tells Cargo we’re installing directly with git, which is what we’re doing here; the locked option is specified in Harper’s documentation, and upon some research, I learned this forces the exact dependency versions specified in Cargo.lock. Without it, cargo might choose newer dependency versions that were not tested or are not specified in the documentation.

    Finally, in Emacs, we want to tell eglot where to find Harper:

        (when (eq system-type 'gnu/linux)
          (add-to-list 'exec-path (expand-file-name "~/.cargo/bin")))
    

    In my case, since I use the same config on my Mac, I want this to run only on Linux. On my Mac, Harper is installed without all these shenanigans directly from Homebrew, which also keeps it up to date. This is added to the same config block I specified in my earlier post. It now looks like this:

                (with-eval-after-load 'eglot
                  (add-to-list 'eglot-server-programs
                               '(org-mode . ("harper-ls" "--stdio"))))
        
                 (setq-default eglot-workspace-configuration
                            '(:harper-ls (:dialect "American" :linters (:LongSentences :json-false :AvoidCurses :json-false))))
    
          ;; Besides choosing American as the language, I also want to ignore long sentences (the main issue is that it hides other errors nested in those), and I also want Harper not to tell me when it thinks something is offensive. I'm a big boy/an old fart. The full list of these options is in https://writewithharper.com/docs/rules. It needs to be nested inside the :linters option.
        
        (when (eq system-type 'gnu/linux)
          (add-to-list 'exec-path (expand-file-name "~/.cargo/bin")))
    

    In the future, when I need to update:

        cargo install --git https://github.com/Automattic/harper harper-ls --locked
    

    Now, Harper works as it should on my Linux Desktop. Another geeky weekend project.

    Using Denote for Email: A manual workflow

    As I started to write more emails to other bloggers, the annoyance with macOS’ built-in email client grew. It wasn’t just the fact that it has small text that’s hard on the eyes especially on the harsh white background anymore; it just started to feel restricting.

    Emacs is my natural writing environment for longer texts, like blog posts or the kind of emails I end up writing.

    I’ve considered mu4e before, but setting it up seems a daunting overkill: the place I would benefit from mu4e is work, but I’m blocked by Microsoft-only 2FA authentication, so I have to stick with Outlook; meanwhile, for the three or so emails I write to other bloggers, it doesn’t require such heavy lifting.

    One day about two weeks ago, I just fired up Denote, and suddenly it clicked. Denote, when you invoke it for a new note, asks for a directory - so I created an email directory in my parent Notes folder, and started writing. For a title, I use the subject, and the keyword is reserved for the recipient.

    Now my eyes thank me again, as some of these emails can take an hour (and more even) to write. Links are a breeze to include, and quotes - which I use heavily in emails - are just a keyboard press away. It also looks nice when I go to the email directory and see all my drafts there, organized nicely as Denote knows how to do.

    Since Denote doesn’t handle emails, for this I simply export the org file to HTML, and then with Dired (which opens in the same directory as the note I’m writing by default), I open the HTML file with my browser. From there, I copy-paste into Apple Mail, which acts as a proofread enhancer with Grammarly going to work there (this is something I’d miss if I were to use mu4e, though I could probably use Harper).

    It’s a bit of a manual process, and I do need to delete the HTML files from the email directory every now and then, but for now it’s fine. It’s probably easy enough to create some shortcut that will open these HTML files directly with Mail instead of copy-pasting1 though.

    Footnotes

    1 Opening an HTML file with Dired with ! open -a Mail would make sense, but it opens Mail with the HTML file as an attachment, not as the body of the text.

    Journelly and OSM for Emacs are good together

    I mentioned OSM for emacs briefly before, but I haven’t played with it much. That’s because the maps never showed up correctly in the buffer: the map tiles were not aligned correctly and some appeared blank.

    As it turns out, someone else had this problem and also found the culprit: visual-line-mode. I have it turned on by default as the majority of my work in Emacs involves org-mode and I need my lines wrapped in the buffer. With visual-line-mode disabled, OSM works as expected, including zooming in and out. Good stuff.

    Now that I fixed OSM, I was wondering about something else I wanted to do for a while: having Journelly’s latitude and longitude fed automatically to OSM in Emacs, so I can view the location on a map.

    Journelly captures locations and weather information for each note and stores those under properties, like so:

    PROPERTIES:
    :LATITUDE: ##.##########
    :LONGITUDE: ##.##########
    :WEATHER_TEMPERATURE: 62.1°F
    :WEATHER_CONDITION: Cloudy
    :WEATHER_SYMBOL: cloud
    :END:
    

    The OSM function that calls for those is osm-goto.

    So what we need is a simple function to feed the properties values directly:

    (defun jtr-goto-from-properties ()
    (interactive)
    (let ((lat (org-entry-get (point) "LATITUDE"))
    (lon (org-entry-get (point) "LONGITUDE")))
    (if (and lat lon)
    (osm-goto (string-to-number lat) (string-to-number lon) osm-default-zoom)
    (message "No LATITUDE/LONGITUDE properties found on this entry"))))
    

    This is an interactive function that I use when I’m standing on the header in Journelly I want to see on a map. It’s quick and works well. Now I can use my Journelly entries, which are already in org-mode, as a base for a post with a map tile inside Emacs. OSM doesn’t have a native function to export an image, but since I usually want to annotate the image anyway before I make a post out of it, a regular screen-capture app is a good solution, at least for now.

Older Posts →