Categories: geek » emacs

View topic page - RSS - Atom - Subscribe via email
Recommended links:

My Emacs writing experience

| writing, emacs, org

I've been enjoying reading people's responses to the Emacs Carnival July theme of writing experience. I know I don't need complicated tools to write. People can write in composition notebooks and on typewriters. But I have fun learning more about the Emacs text editor and tweaking it to support me. Writing is one of the ways I think, and I want to think better. I'll start with the kinds of things I write in my public and private notes, and then I'll think about Emacs specifically.

Types of notes

Text from sketch

What kinds of posts do I write? How? Improvements?

2025-07-25-05

  • Emacs News
    • why: collecting & connecting → fun!
    • how:
      • phone: Reddit: upvotes
      • YouTube: playlist
    • RSS
    • Mastodon: Scrape boosts?
    • Dedupe, categorize: classifier?
    • Blog
    • Mailing list
    • emacs.tv
    • emacslife.com/calendar
  • Bike Brigade newsletter
    • why: help out, connect
    • Reddit + X + Slack -> Slack canvas -> MailChimp
    • Need more regular last-min sweep
    • Copying from Slack sucks; Google Docs?
  • Tech notes
    • Why: figure things out, remember, share
    • code
    • literate programming: notes + code
    • debugger?
    • more notes?
    • thinking out loud?
  • Life reflections
    • Why: figure things out, remember
    • tangled thoughts
    • sketch: habit? more doodles
    • audio braindump
    • snippets on phone
    • learning to think
    • laptop: write
    • audio input?
    • themes, thoughts
      • LLM? reflection questions, topics to learn more about
  • Book notes
    • Why: study, remember, share
    • paper: draw while reading
    • e-book: highlight
      • quotes
    • sketch
      • smaller chunks?
    • blog
  • Monthly/yearly reviews
    • Why: plan, remember
    • phone: daily journal
    • tablet: draw moment of the day
    • phone: time records
    • Emacs: raw data
    • themes, next steps: LLM? reflection questions?
    • blog post

Emacs News

I put together a weekly list of categorized links about the interesting ways people use Emacs. This takes me about an hour or two each week. I enjoy collecting all these little examples of people's curiosity and ingenuity. Organizing the links into a list helps people find things they might be interested in and connect with other people.

I start by skimming r/emacs and r/orgmode on my phone, upvoting posts that I want to include. I also search YouTube and add videos to an Emacs News playlist. I review aggregated posts from Planet Emacslife. I have an Emacs Lisp function that collects all the data and formats them as a list, with all the items at the same level.

For Mastodon, I check #emacs search results from a few different servers. I have a keyboard shortcut that boosts a post and captures the text to an Org Mode file, and then I have another function that prompts me to summarize toots, defaulting to the title of the first link. I have more functions that help me detect duplicates and categorize links. I use ox-11ty to export the post to my blog, which uses the Eleventy static site generator. I also use emacstv.el to add the videos to the Org file I use for emacs.tv.

Some ways to improve this:

  • I probably have enough data that it might be interesting to learn how to write a classifier. On the other hand, regular expression matches on the titles get most of them correctly, so that might be sufficient.
  • YouTube videos are a little annoying to go through because of interface limitations and unrelated or low-effort videos. I can probably figure out something that checks the RSS feeds of various channels.

Bike Brigade newsletter

I also put together a weekly newsletter for Bike Brigade, which coordinates volunteer cyclists to deliver food bank hampers and other essentials. Writing this mostly involves collecting ideas from a number of social media feeds as well as the other volunteers in the community, putting together a draft, and then copying it over to Mailchimp. I'm still figuring out my timing and workflows so that I can stay on top of last-minute requests coming in from people on Slack, and so that I can repurpose newsletter items as updates in the Facebook group or maybe even a blog. If I set aside some regular time to work on things, like a Sunday morning sweep for last-minute requests, that might make it easier to work with other people.

Tech notes

I like coding, and I come up with lots of ideas as I use my computer. I enjoy figuring out workflow tweaks like opening lots of URLs in a region or transforming HTML clipboard contents. My Org files have accumulated quite a few. My main limiting factor here is actually sitting down to make those things happen. Fortunately, I have recently discovered that it's possible for me to spend an hour or two a day playing Stardew Valley, so I can swap some of that time for Emacs tweaking instead. Coding doesn't handle interruptions as well as playing does, but taking notes along the way might be able to help with that. I can jump to the section of my Org file with the ideas I wanted to save for more focus time, pick something from that screen, and get right to it.

Other things that might help me do this more effectively would be:

  • getting better at using my tools (debugger, documentation, completion, etc.),
  • taking the opportunity to plug in an external monitor, and
  • using my non-computer time to mull over the ideas so that I can hit the ground running.

I like taking notes at virtual meetups. I usually do this with Etherpad so that other people can contribute to the notes too. I don't have a real-time read-write Emacs interface to this yet (that would be way cool!), but I do have some functions for working with the Etherpad text.

Life reflections

When I notice something I want to figure out or remember, I use sketches, audio braindumps, or typing to start to untangle that thought. Sometimes I use all three, shifting from one tool to another depending on what I can do at the moment. I have a pretty comfortable workflow for converting sketches (Google Vision) or audio (OpenAI Whisper) to text so that I can work with it more easily, and I'm sure that will get even smoother as the technology improves. I switch from one tool to another as I figure out the shape of my thoughts.

Maybe I can use microblogging to let smaller ideas out into the world, just in case conversations build them up into more interesting ideas. I don't quite trust my ability to manage my GoToSocial instance yet (backups? upgrades?), so that might be a good reason to use a weekly or monthly review to revisit and archive those posts in plain text.

I've been reading my on this day list of blog posts and sketches more regularly now that it's in my feed reader. I like the way this helps me revisit old thoughts, and I've saved a few that I want to follow up on. It feels good to build on a thought over time.

I'd like to do more of this remembering and thinking out loud because memories are fleeting. Maybe developing more trust in my private journals and files will help. (Gotta have those backups!) Then I'll be more comfortable writing about the things we're figuring out about life while also respecting A+ and W-'s privacy, and I can post the stuff I'm figuring out about my life that I'm okay with sharing. I might think something is straightforward, like A+'s progress in learning how to swim. I want to write about how that's a microcosm of how she's learning how to learn more independently and my changing role in supporting her. Still, she might have other opinions about my sharing that, either now or later on. I can still reflect on it and keep that in a private journal as we figure things out together.

Even though parenting takes up most of my time and attention at the moment, it will eventually take less. There are plenty of things for me to learn about and share outside parenting, like biking, gardening, and sewing. I've got books to read and ideas to try out.

I'm experimenting with doing more writing on my phone so that I can get better at using these little bits of time. Swiping letters on a keyboard is reasonably fast, and the bottleneck is my thinking time anyway. I use Orgzly Revived so that Syncthing can synchronize it with my Org Mode files on my laptop when I get back home. There are occasional conflicts, but since I mostly add to an inbox.org when I'm on my phone, the conflicts are usually easy to resolve.

Adding doodles to my reflections can make them more fun. I can draw stick figures from scratch, and I can also trace my photos using the iPad as a way to add visual anchors and practise drawing. If I get the hang of using a smaller portion of my screen like the way I used to draw index cards, that might make thoughts more granular and easier to complete.

When I write on my computer, I often use writeroom-mode so that things feel less cluttered. I like having big margins and short lines. I have hl-line-mode turned on to help me focus on the current paragraph. This seems to work reasonably well.

2025-07-26_00-33-44.png
Figure 1: Screenshot showing writeroom-mode and hl-line-mode

Monthly and yearly reviews

I like the rhythm of drawing daily moments and keeping a web-based journal of brief descriptions of our day. I like how I've been digging into them deeper to reflect on themes. The monthly drawings and posts make it easier to review a whole year. Maybe someday I'll get back to weekly reviews as well, but for now, this is working fine.

My journal entries do a decent job of capturing the facts of our days: where we went, what we did. Maybe spending more time writing life reflections can help me capture more of what goes on in my head and what I want to learn more about.

Book notes

I draw single-page summaries of books I like because they're easier to remember and share. E-books are convenient because I can highlight text and extract that data even after I've returned the book, but I can also retype things from paper books or use the text recognition feature on my phone camera. I draw the summaries on my iPad using Noteful, and then I run it through my Google Vision workflow to convert the text from it so that I can include it in a blog post.

The main limiting factor here is my patience in reading a book. There are so many other wonderful things to explore, and sometimes it feels like books have a bit of filler. When I have a clear topic I'm curious about or a well-written book to enjoy, it's easier to study a book and make notes.

Emacs workflow thoughts

Aside from considering the different types of writing I do, I've also been thinking about the mechanics of writing in Emacs. Sanding down the rough parts of my workflow makes writing more enjoyable, and sometimes a small tweak lets me squeeze more writing into fragments of time.

There are more commands I want to call than there are keyboard shortcuts I can remember. I tend to use M-x to call commands by name a lot, and it really helps to have some kind of completion (I use vertico) and orderless matching.

I'm experimenting with more voice input because that lets me braindump ideas quickly on my phone. Long dictation sessions are a little difficult to edit. Maybe shorter snippets using the voice input mode on the phone keyboard will let me flesh out parts of my outline. I wonder if the same kind of quick input might be handy on my computer. I'm trying out whisper.el with my Bluetooth earbuds. Dictating tends to be stop-and-go, since I feel self-conscious about dictating when other people are around and I probably only have solo time late at night.

Misrecognized words can be annoying to correct on my phone. They're much easier to fix on my computer. Some corrections are pretty common, like changing Emax to Emacs. I wrote some code for fixing common errors (my-subed-fix-common-errors), but I don't use this often enough to have it in my muscle memory. I probably need to tweak this so that it's a bit more interactive and trustworthy.

When I see a word I want to change, I jump to it with C-s (isearch-forward) or C-r (isearch-backward), or I navigate to it with M-f (forward-word). I want to get the hang of using Avy because of Karthik's awesome post about it. That post is from 2021 and I still haven't gotten used to it. I probably just need deliberate practice using the shortcut I've mapped to M-j (avy-goto-char-timer). Or maybe I just don't do this kind of navigation enough yet to justify this micro-optimization (no matter how neat it could be), and isearch is fine for now.

Sometimes I want to work with sentences. expand-region is another thing I want to get used to. I've bound C-= to er/expand-region from that package. Then I should be able to easily kill the text and type a replacement or move things around. In the meantime, I can usually remember to use my keyboard shortcut of M-z for avy-zap-up-to-char-dwim for deleting something.

Even in vanilla Emacs, there's so much that I think I'll enjoy getting the hang of. oantolin's post on his writing experience helped me learn about M-E, which marks the region from the point to the end of the sentence and is a natural extension from M-e. Similarly, M-F selects the next word. I could use this kind of shift-selection more. I occasionally remember to transpose words with M-t, but I've been cutting and pasting sentences when I could've been using transpose-sentences all this time. I'm going to add (keymap-global-set "M-T" #'transpose-sentences) to my config and see if I remember it.

I like using Org Mode headings to collapse long text into a quick overview so I can see the big picture, and they're also handy for making tables of contents. It might be neat to have one more level of overview below that, maybe displaying only the first line of each paragraph. In the meantime, I can use toggle-truncate-lines to get that sort of view.

If I'm having a hard time fitting the whole shape of a thought into my working memory, I sometimes find it easier to work with plain list outlines that go all the way down to sentences instead of working with paragraphs. I can expand/collapse items and move them around easily using Org's commands for list items. In addition, org-toggle-item toggles between items and plain text, and org-toggle-heading can turn items into headings.

I could probably write a command that toggles a whole section between an outline and a collection of paragraphs. The outline would be a plain list with two levels. The top level items would be the starting sentences of each paragraph, and each sentence after that would be a list item underneath it. Sometimes I use actual lists. Maybe those would be a third level. Then I can use Org Mode's handy list management commands even when a draft is further along. Alternatively, maybe I can use M-S-left and M-S-right to move sentences around in a paragraph.

Sometimes I write something and then change my mind about including it. Right now, I tend to either use org-capture to save it or put it under a heading and then refile it to my Scraps subtree, but the palimpsest approach might be interesting. Maybe a shortcut to stash the current paragraph somewhere…

I use custom Org link types to make it easier to link to topics, project files, parts of my Emacs configuration, blog posts, sketches, videos, and more. It's handy to have completion, and I can define how I want them to be exported or followed.

Custom Org link types also let me use Embark for context-sensitive actions. For example, I have a command for adding categories to a blog post when my cursor is on a link to the post, which is handy when I've made a list of matching posts. Embark is also convenient for doing things from other commands. It's nice being able to use C-. i to insert whatever's in the minibuffer, so I can use that from C-h f (describe-function), C-h v (describe-variable), or other commands.

I also define custom Org block types using org-special-block-extras. This lets me easily make things like collapsible sections with summaries.

I want to get better at diagrams and charts using things like graphviz, mermaidjs, matplotlib, and seaborn. I usually end up searching for an example I can build on and then try to tweak it. Sometimes I just draw something on my iPad and stick it in. It's fine. I think it would be good to learn computer-based diagramming and charting, though. They can be easier to update and re-layout when I realize I've forgotten to add something to the graph.

Figuring out the proper syntax for diagrams and charts might be one of the reasonable use cases for large-language models, actually. I'm on the fence about LLMs in general. I sometimes use claude.ai for dealing with the occasional tip of the tongue situation like "What's a word or phrase that describes…" and for catching when I've forgotten to finish a sentence. I don't think I can get it to think or write like me yet. Besides, I like doing the thinking and writing.

I love reading about other people's workflows. If they share their code, that's fantastic, but even descriptions of ideas are fine. I learn so many things from the blog posts I come across on Planet Emacslife in the process of putting together Emacs News. I also periodically go through documentation like the Org Mode manual or release notes, and I always learn something new each time.

This post was really hard to write! I keep thinking of things I want to start tweaking. I treat Emacs-tweaking as a fun hobby that sometimes happens to make things better for me or for other people, so it's okay to capture lots of ideas to explore later on. Sometimes something is just a quick 5-minute hack. Sometimes I end up delving into the source code, which is easy to do because hey, it's Emacs. It's comforting and inspiring to be surrounded by all this parenthetical evidence of other people's thinking about their workflows.

Each type of writing helps me with a different type of thinking, and each config tweak makes thoughts flow more smoothly. I'm looking forward to learning how to think better, one note at a time.

Check out the Emacs Carnival July theme: writing experience post for more Emacs ideas. Thanks to Greg Newman for hosting!

View org source for this post

Finding the shape of my thoughts

| emacs, org, writing

Text from sketch

Finding the shape of my thoughts 2025-07-23-02

I have a hard time following a thought from beginning to end.

Some people are like this and have figured out things that work well for them.

Challenges:

  • too much or not enough
  • one more thing; rabbit holes
  • dangling thoughts

iPad

  • shape of thought
    • topics?
    • enough?
    • flow?
  • metaphors, visual frameworks?
  • zooming in? links?
  • text boxes?

Phone

  • outline, snippets, placeholders
  • outline?
  • short dictation?
  • Keyboard?

Laptop

  • fleshing out: code, links, etc.
  • Zettelkasten?
  • editing audio braindump?
  • managing idea pipeline?
  • leave TODOS, mark them

Overall:

  • Develop thoughts in conversation
  • Use the constraints
  • Get the ball rolling

I want to write more. Writing better can follow, with practice and reflection. But writing is challenging. Coming up with ideas is not the hard part. It's finishing them without getting distracted by the hundred other ideas I come up with along the way. I have a hard time following one thought from beginning to end. My mind likes to flit around, jumping from one idea to another. Even when I make an outline, I tend to add to one section, wander over to another, come back to the first, get very deep into one section and decide it's probably its own blog post, and so on. Sometimes I want to say too much to fit into a blog post. Sometimes I start writing and find that I don't have enough to say yet. Sometimes I keep getting distracted by one more thing I want to do before I feel like I can finish the post. Sometimes an idea turns out to be a deep rabbit hole. Sometimes I can rein in those thoughts by using TODOs and next steps and somedays, but then I have all these threads left dangling and it doesn't quite feel right.

Fortunately, other people have figured out how to work with things like this. Roland Allen shares an example in The Notebook: A History of Thinking on Paper (2023):

P117. More commonly, [Leonardo da Vinci] expresses annoyance at his own distractability or perceived lack of progress. "Alas, this will never get anything done" is a theme that recurs in several notebooks.

Asked about the experience of looking at the "spine-tingling" notebooks, [Martin] Kemp employs strikingly kinetic language. "As material objects, they have an extraordinary intensity, little notebooks with this pretty tiny writing, done at great speed, great urgency, a kind of desperate intensity, when something else crowds in he has to jot it down, he goes back to the original thought, he gets diverted, he comes back to that page and will write some more… it's a very manic business."

My life is much smaller scale, but it's nice to know that other people have figured out or are figuring out how to work with how they are. For example, I've been drafting a post for July's Emacs Carnival theme of writing experience. Along the way, I found myself adding my blog posts as a consult-omni source, using that to add URLs to link placeholders, and writing this post about non-linearity. I'm very slowly learning to break those up into their own posts, or maybe even just save the idea as a TODO so that I can finish the thing that I'm writing before I get distracted by figuring out something else. It's easier to work with the grain than against it, so I follow wherever my curiosity leads, and then figure out what chunks I can break into posts.

I'm also coming to terms with the fact that I don't know what I'm writing until I write it and tweak it. No Athena springing forth fully-formed. The ideas develop in conversation: me with my sketches and text, and if I'm lucky, other people too. Sometimes there isn't enough there yet, so I need to put the idea aside for now. Sometimes there's too much I want to say, so I need to select things to focus on.

When I have an idea I want to write about, I like to start with drawing in Noteful on my iPad: sometimes a mind map, sometimes just words and phrases scattered on a page until I figure out which things are close to each other. I can select things with a lasso and move them closer together, and I can use a highlighter to choose things to focus on. This helps me get a sense of what I want to write about and what examples I want to use. Then I can take a step back and figure out the order that makes sense for the post.

My starting sketch for this post:

Text from sketch

Non-linear writing 2025-07-23-01

  • 1. I have a hard time following one thought from beginning to end.
    • My mind likes to flit around.
    • Other people have figured out how
  • 2. Challenges:
    • trying to cram in too much
    • one more thing
    • yak-shaving / rabbitholes
    • dangling threads
  • 3. iPad
    • Map
    • enough
    • not dense
    • starting points
    • Sketch
    • Order
    • crossing out?
    • Structure?
    • rough sketch vs shareable
    • hyperlinks?
      • Zoom in
        • finer pen, actual zoom?
        • This is as small as it gets. Extra details?
    • Bluetooth keyboard?
    • Beorg, Plain Org?
    • Airdroid or hotspot?
    • visual frameworks, David Gray
    • I'm experimenting with using Noteful's text boxes so that I can quickly dictate the thought
  • 4. Phone
    • Snippets
    • short dictation?
    • outline - collapsible?
    • Sometimes I only have my phone with me. I can make a quick outline in orgzly revived and then fill in paragraphs jumping around as needed. Sometimes I feel like I'm going to lose track of the dangling threads, especially if they're in the middle of a paragraph so maybe a Todo marker might be good for that.
    • Outline
  • 5. Laptop
    • break out smaller chunks into their own posts.
    • leave TODOS
    • Zettelkasten
    • mention Emacs Conf talk about writing; also org-roam
  • audio braindump
    • tangled
    • editing?
    • needs computer for now
    • LLM?
    • Shorter is prob. more useful
  • drawing metaphor?
    • painting?
    • mark-making
    • bounds, shape
    • gradually fill in
  • move ideas for improvement to the different sections

Sometimes ideas peter out at this stage, when I find that I don't have much to say yet about it. I organize my Noteful notes by month, so I just leave the unfinished sketch there. I could probably tag it to make it findable again, but I usually end up moving on to other thoughts instead. If I want to revisit an idea later on, it's often easier to just make a new map. There are so many ideas I can explore, so it's good to quickly find out when I don't have enough to say about something. It might make more sense to me later on.

If I can figure out the rough shape of an idea and I feel like I have enough thoughts to fill it with, then it's time to figure out words. Swiping on an onscreen keyboard is more comfortable on my phone than on the iPad, although maybe that's just a matter of getting used to it. If I've developed the idea enough to have a clear flow, I can write the outline without referring to my sketch. If I happen to have a flat surface like a table, I can write while looking at my drawing. Once I have an outline on my phone, I can fill it in with paragraphs.

Sometimes it's easier for me to dictate than to type, like when I'm watering the plants. I use OpenAI Whisper to transcribe the recordings. The speech recognition is pretty accurate, but I have a lot of false starts, tangents, and rephrasing. My thoughts are rough and tangled, tripping over each other on their way out, but saying them out loud gets them down into a form I can look at. I still need to do a fair bit of work to clean up the text and organize it into my outline. Some people use large-language models to organize raw transcripts, but I haven't quite figured out a good prompt that turns a wall of raw text into something that's easy to include in my draft while retaining my voice. At the moment, I'd rather just manually go over my transcript for ideas I want to include and phrasings I might want to keep. As I massage the braindump into a post, I notice other things I want to add or rephrase. Maybe I'll get the hang of using voice input mode to dictate shorter snippets so that I can do it on my phone or iPad instead of needing computer time.

When I'm ready to expand these fragments into full posts, it's easiest to write on the computer, especially if I want to look up documentation or write code. My Orgzly Revived notes generally synchronize seamlessly with Org Mode in Emacs via Syncthing, aside from the occasional sync conflict that I need to resolve. Then I can build on whatever I started jotting down on my phone. Since I type quickly, thinking is the real bottleneck. If I've thought about things enough through my sketches or phone drafts, writing on my computer goes faster.

I find it easier to assemble a thought out of smaller snippets than to write from scratch, which is why Zettelkasten appeals to me. I still want to figure out some kind of approximate search, or even an exact search that can check Org entry text in a reasonable way. (Maybe org-ql…) My notes are not nearly as organized as people's org-roam constellations, but I'm starting to be able to pull together snippets of drafts, quotes from books, links to previous blog posts, and things I've come across in my reading.

Some ideas stall at this stage, too. M-x occur for "^\* " shows 65 top-level headings in my posts.org drafts. Sometimes it's because I've run into something I haven't figured out yet. Sometimes it's because the thoughts are still tangled. Sometimes it's because I've gotten distracted by other things, or a different approach to the same topic. I generally work on the more recent ones that are still in my mind. I also have a tree-map visualization that gives me a sense of the heft of each draft, in case the accumulation of words helps nudge me to finish it. It's okay for thoughts to take a while.

2025-07-23_20-18-40.png
Figure 1: Treemap visualization of my posts.org

So my iPad is for sketching out the thought, my phone is for writing on the go, and my computer is for fleshing it all out. How could I make this better?

  • iPad:
    • What if I use more structure, visual frameworks, or metaphors, instead of starting from a totally blank page? That can help suggest things to think about, like the way a 2x2 matrix can help organize contrasts.
    • I can zoom in and write with a thin stroke to add more detail. If I need even more space, I can link to a separate page.
    • I can add textboxes and use voice input to quickly capture fragments of ideas as I draw.
  • Phone:
    • I can explore the outline tools of Beorg, Plain Org, or Orgzly Revived to see if I can get the hang of using them when I'm away from my computer.
    • I can try the voice input on my phone. To keep the flow going, I need to resist the urge to correct misrecognized words as long as things are somewhat understandable.
    • Maybe I can try bringing a Bluetooth keyboard to playdates where I'm likely to be near a table.
  • Writing:
    • I can run more ideas through my audio braindumping process so that I can improve my workflow.
    • I can use Org Mode TODO states or tags to manage my idea pipeline so that I can keep track of posts that are almost there.
    • I can be more ruthless about parking an idea as a TODO or a next step instead of feeling like I need to go write that post before I can finish this one. This might also help me write in smaller chunks.

Even if I have to rewrite chunks as I figure things out, that's not a waste. That's just part of how thoughts develop. I'm constrained by the tools that I use and the fragments of time that I have, but I can use those constraints to help me break things down into manageable pieces. If I take advantage of those little bits of time to get the ball rolling, writing at the computer becomes much easier and more fun. This is the kind of brain I've got, and I enjoy learning more about working with it.

View org source for this post

Using web searches and bookmarks to quickly link placeholders in Org Mode

Posted: - Modified: | org, emacs

[2025-07-23 Wed]: Handle Org link updates from the middle of a link.

I want to make it easy to write with more hyperlinks. This lets me develop thoughts over time, building them out of small chunks that I can squeeze into my day. It also makes it easy for you to dig into things that pique your curiosity without wading through lots of irrelevant details. Looking up the right URLs as I go along tends to disrupt my train of thought. I want to make it easier to write down my thoughts first and then go back and add the URLs. I might have 5 links in a post. I might have 15. Sounds like an automation opportunity.

If I use double square brackets around text to indicate where I want to add links, Orgzly Revived and Org Mode both display those as links, so I can preview what the paragraph might feel like. They're valid links. Org Mode prompts me to create new headings if I follow them. I never use these types of links to point to headings, though. Since I only use custom IDs for links, any [[some text]] links must be a placeholder waiting for a URL. I want to turn [[some text]] into something like [[https://example.com][some text]] in the Org Mode markup, which gets exported as a hyperlink like this: some text. To figure out the target, I might search the web, my blog, or my bookmarks, or use the custom link types I've defined for Org Mode with their own completion functions.

Most of my targets can be found with a web search. I can do that with consult-omni with a default search based on the link text, prioritizing my bookmarks and blog posts. Then I don't even have to retype the search keywords.

(defun my-org-set-link-target-with-search ()
  "Replace the current link's target with a web search.
Assume the target is actually supposed to be the description.  For
example, if the link is [[some text]], do a web search for 'some text',
prompt for the link to use as the target, and move 'some text' to the
description."
  (interactive)
  (let* ((bracket-pos (org-in-regexp org-link-bracket-re))
         (bracket-target (match-string 1))
         (bracket-desc (match-string 2))
         result)
    (when (and bracket-pos bracket-target
               (null bracket-desc)
               ;; try to trigger only when the target is plain text and doesn't have a protocol
               (not (string-match ":" bracket-target)))
      ;; we're in a bracketed link with no description and the target doesn't look like a link;
      ;; likely I've actually added the text for the description and now we need to include the link
      (let ((link (consult-omni bracket-target nil nil t)))
        (cond
         ((get-text-property 0 :url link)
          (setq result (org-link-make-string (get-text-property 0 :url link)
                                             bracket-target)))
         ((string-match ":" link)       ; might be a URL
          (setq result (org-link-make-string link bracket-target))))
        (when result
          (delete-region (car bracket-pos) (cdr bracket-pos))
          (insert result)
          result)))))

This is what that looks like:

Screencast: filling in a link URL based on the text

consult-omni shows me the title, first part of the URL, and a few words from the page. C-o in consult-omni previews search results, which could be handy.

Sometimes a web search isn't the fastest way to find something. Some links might be to my Emacs configuration, project files, or other things for which I've written custom Org link types. I can store links to those and insert them, or I can choose those with completion.

(defun my-org-set-link-target-with-org-completion ()
  "Replace the current link's target with `org-insert-link' completion.
Assume the target is actually supposed to be the description.  For
example, if the link is [[some text]], do a web search for 'some text',
prompt for the link to use as the target, and move 'some text' to the
description."
  (interactive)
  (let* ((bracket-pos (org-in-regexp org-link-bracket-re))
         (bracket-target (match-string 1))
         (bracket-desc (match-string 2))
         result)
    (when (and bracket-pos bracket-target
               (null bracket-desc)
               ;; try to trigger only when the target is plain text and doesn't have a protocol
               (not (string-match ":" bracket-target))
               (org-element-lineage (org-element-context) '(link) t)) ; ignore text in code blocks, etc.
      ;; we're in a bracketed link with no description and the target doesn't look like a link;
      ;; likely I've actually added the text for the description and now we need to include the link.
      ;; This is a hack so that we don't have to delete the link until the new link has been inserted
      ;; since org-insert-link doesn' tbreak out the link prompting code into a smaller function.
      (let ((org-link-bracket-re "{{{}}}"))
        (goto-char (cdr bracket-pos))
        (org-insert-link nil nil bracket-target))
      (delete-region (car bracket-pos) (cdr bracket-pos)))))

Here's an example:

Screencast showing how to specify a link with completion

If I decide a web search isn't the best way to find the target, I can use <up> and RET to get out of the consult-omni web search and then go to the usual Org link completion interface.

(defun my-org-set-link-target-dwim ()
  (interactive)
  (or (my-org-set-link-target-with-search)
      (my-org-set-link-target-with-org-completion)))

If I can't find the page either way, I can use C-g to cancel the prompt, look around some more, and get the URL. When I go back to the web search prompt for the link target, I can press <up> RET to switch to the Org completion mode, and then paste in the URL with C-y or type it in. Not elegant, but it will do.

I can now look for untargeted links and process each one. The (undo-boundary) in the function means I can undo one link at a time if I need to.

(defun my-org-scan-for-untargeted-links ()
  "Look for [[some text]] and prompt for the actual targets."
  (interactive)
  (while (re-search-forward org-link-bracket-re nil t)
    (when (and
           (not (match-string 2))
           (and (match-string 1) (not (string-match ":" (match-string 1))))
           (org-element-lineage (org-element-context) '(link) t)) ; ignore text in code blocks, etc.
      (undo-boundary)
      (my-org-set-link-target-dwim))))

Here's how that works:

Screencast showing how I can scan through the text for untargeted links and update them

Play by play
  1. 0:00:00 I started with a search for "build thoughts out of smaller chunks" and deleted the default text to put in "developing" so that I could select my post on developing thoughts further.
  2. 0:00:08 I linked to Orgzly Revived from my bookmarks.
  3. 0:00:10 I selected the Org Mode website from the Google search results.
  4. 0:00:12 I selected the consult-omni Github page from the search results.
  5. 0:00:18 I used <up> RET to skip the web search instead of selecting a candidate, and then I selected the bookmarks section of my configuration using Org link completion.
  6. 0:00:25 I changed the search query and selected the post about using consult-omni with blog posts.
  7. 0:00:31 I chose the p-search Github repository from the Google search results.

If my-org-scan-for-untargeted-links doesn't find anything, then the post is probably ready to go (at least in terms of links). That might help avoid accidentally posting placeholders. I'm going to experiment with going back to the default of having org-export-with-broken-links be nil again, so that's another safety net that should catch placeholders before they get published.

Next steps

I can look at the code for the web search and add the same kind of preview function for my bookmarks and blog posts.

I can modify my C-c C-l binding (my-org-insert-link-dwim) to have the same kind of behaviour: do a web/bookmark/blog search first, and fall back to Org link completion.

Someday it might be nice to add a font-locking rule so that links without proper targets can be shown in a different colour. org-export-with-broken-links and org-link-search both know about these types of links, so there might be a way to figure out font-locking.

I might not use the exact words from the title, so it would be good to be able to specify additional keywords and rank by relevance. The p-search talk from EmacsConf 2024 showed a possible approach that I haven't dug into yet. If I want to get really fancy, it would be neat to use the embedding of the link text to look up the most similar things (blog posts, bookmarks) and use that as the default.

I'm looking forward to experimenting with this. I think it will simplify linking to things when I'm editing my drafts on my computer. That way, it might be easier for me to write about whatever nifty idea I'm curious about while helping people pick up whatever background information they need to make sense of it all.

This is part of my Emacs configuration.
View org source for this post

Finding my blog posts with consult-omni

Posted: - Modified: | emacs

Sometimes I just want to quickly get to a blog post by title. I use consult-omni for quick web searches that I can jump to or insert as a link. Sure, I can limit this search to my blog by specifying site:sachachua.com or using the code I wrote to search my blog, notes, and sketches with consult-ripgrep and consult-omni, but the search sometimes gets confused by other text on the page. When I publish my blog with Eleventy, I also create a JSON file with all my blog post URLs and titles. Here's how I can use that data as a consult-omni source.

(defun my-consult-omni-blog-data ()
  (let ((base (replace-regexp-in-string "/$" "" my-blog-base-url))
        (json-object-type 'alist)
        (json-array-type 'list))
    (mapcar
     (lambda (o)
       (list :url (concat base (alist-get 'permalink o))
             :title (alist-get 'title o)
             :date (alist-get 'date o)))
     (sort (json-read-file "~/sync/static-blog/_site/blog/all/index.json")
           (lambda (a b)
             (string< (or (alist-get 'date b) "")
                      (or (alist-get 'date a) "")))))))
(unless (get 'my-consult-omni-blog-data :memoize-original-function)
  (memoize #'my-consult-omni-blog-data "5 minutes"))

(defun my-consult-omni-blog-titles-builder (input &rest args &key callback &allow-other-keys)
  (let* ((quoted (when input (regexp-quote input)))
         (list
          (if quoted
              (seq-filter
               (lambda (o)
                 ;; TODO: Someday figure out orderless?
                 (string-match quoted (concat (plist-get o :title) " - " (plist-get o :title))))
               (my-consult-omni-blog-data))
            (my-consult-omni-blog-data)))
         (candidates
          (mapcar
           (lambda (o)
             (propertize
              (concat (plist-get o :title))
              :source "Blog"
              :date (plist-get o :date)
              :title (plist-get o :title)
              :url (plist-get o :url)))
           (if quoted (seq-take list 3) list))))
    (when callback (funcall callback candidates))
    candidates))

(defun my-consult-omni-blog-annotation (s)
  (format " (%s)"
          (propertize (substring (or (get-text-property 0 :date s) "") 0 4)
                      'face 'completions-annotations)))

(with-eval-after-load 'consult-omni
  (consult-omni-define-source
   "Blog"
   :narrow-char ?b
   :type 'sync
   :request #'my-consult-omni-blog-titles-builder
   :on-return 'my-consult-org-bookmark-visit
   :group #'consult-omni--group-function
   :annotate #'my-consult-omni-blog-annotation
   :min-input 3
   :sort nil
   :require-match t))

Here's what it looks like by itself when I call consult-omni-my-blog:

2025-07-21_23-24-09.png
Figure 1: Screenshot of a search through my blog post titles

Then I can add it as one of the sources used by consult-omni:

  (setq consult-omni-multi-sources
        '(consult-omni--source-google
          consult-omni--source-my-org-bookmarks
          consult-omni--source-my-blog))

Here's what it looks like when I call consult-omni to search my bookmarks, blog posts, and Google results at the same time.

2025-07-21_23-29-06.png
Figure 2: Screenshot of a consult-omni search with multiple sources

Then I can press RET to open the blog post in my browser or C-. i l to insert the link using Embark (my-consult-omni-embark-insert-link).

Related:

Next steps: I want to find out how to get :sort nil to be respected so that more recent blog posts are listed first. Also, it might be fun to define a similar source for the sections of my Emacs configuration, like the way I can use my dotemacs: Org Mode link type with completion.

This is part of my Emacs configuration.
View org source for this post

2025-07-21 Emacs news

| emacs, emacs-news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Mastodon #emacs, Bluesky #emacs, Hacker News, lobste.rs, lemmy.world, lemmy.ml, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!

View org source for this post

2025-07-14 Emacs news

| emacs, emacs-news

Links from reddit.com/r/emacs, r/orgmode, r/spacemacs, r/planetemacs, Mastodon #emacs, Bluesky #emacs, Hacker News, lobste.rs, programming.dev, lemmy.world, lemmy.ml, planet.emacslife.com, YouTube, the Emacs NEWS file, Emacs Calendar, and emacs-devel. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at sacha@sachachua.com. Thank you!

View org source for this post

Emacs: Open URLs or search the web, plus browse-url-handlers

Posted: - Modified: | emacs, org
  • [2025-07-14 Mon]: Naturally, do this only with text you trust. =)
  • [2025-07-12 Sat]: Use cl-pushnew instead of add-to-list, correct browse-url-browser-browser-function to browse-url-browser-function, and add an example for eww.

On IRC, someone asked for help configuring Emacs to have a keyboard shortcut that would either open the URL at point or search the web for the region or the word at point. I thought this was a great idea that I would find pretty handy too.

Let's write the interactive function that I'll call from my keyboard shortcut.

  • First, let's check if there's an active region. If there isn't, let's assume we're looking at the thing at point (could be a URL, an e-mail address, a filename, or a word).
  • If there are links, open them.
  • Otherwise, if there are e-mail addresses, compose a message with all those email addresses in the "To" header.
  • Are we at a filename? Let's open that.
  • Otherwise, do a web search. Let's make that configurable. Most people will want to use a web browser to search their favorite search engine, such as DuckDuckGo or Google, so we'll make that the default.
(defcustom my-search-web-handler "https://duckduckgo.com/html/?q="
  "How to search. Could be a string that accepts the search query at the end (URL-encoded)
or a function that accepts the text (unencoded)."
  :type '(choice (string :tag "Prefix URL to search engine.")
                 (function :tag "Handler function.")))

(defun my-open-url-or-search-web (&optional text-or-url)
  (interactive (list (if (region-active-p)
                         (buffer-substring (region-beginning) (region-end))
                       (or
                        (and (derived-mode-p 'org-mode)
                             (let ((elem (org-element-context)))
                               (and (eq (org-element-type elem) 'link)
                                    (buffer-substring-no-properties
                                     (org-element-begin elem)
                                     (org-element-end elem)))))
                        (thing-at-point 'url)
                        (thing-at-point 'email)
                        (thing-at-point 'filename)
                        (thing-at-point 'word)))))
    (catch 'done
      (let (links)
        (with-temp-buffer
          (insert text-or-url)
          (org-mode)
          (goto-char (point-min))
          ;; We add all the links to a list first because following them may change the point
          (while (re-search-forward org-any-link-re nil t)
            (cl-pushnew (match-string-no-properties 0) links))
          (when links
            (dolist (link links)
              (org-link-open-from-string link))
            (throw 'done links))
          ;; Try emails
          (while (re-search-forward thing-at-point-email-regexp nil t)
            (cl-pushnew (match-string-no-properties 0) links))
          (when links
            (compose-mail (string-join links ", "))
            (throw 'done links)))
        ;; Open filename if specified, or do a web search
        (cond
         ((ffap-guesser) (find-file-at-point))
         ((functionp my-search-web-handler)
          (funcall my-search-web-handler text-or-url))
         ((stringp my-search-web-handler)
          (browse-url (concat my-search-web-handler (url-hexify-string text-or-url))))))))

I've been really liking how consult-omni lets me do quick searches as I type from within Emacs, which is actually really cool. I've even extended it to search my bookmarks as well, so that I can find things using my words for them and not trust the internet's words for them. So if I wanted to search using consult-omni, this is how I would do it instead.

(setopt my-search-web-handler #'consult-omni)

Now I can bind that to C-c o in my config with this bit of Emacs Lisp.

(keymap-global-set "C-c o" #'my-open-url-or-search-web)

Here's a quick demo:

Screencast showing it in use

Play by play
  1. Opening a URL: https://example.com
  2. Opening several URLs in a region:
  3. Opening several e-mail addresses:
    • test@example.com
    • another.test@example.com
    • maybe also yet.another.test@example.com
  4. A filename
    • ~/.config/emacs/init.el
  5. With DuckDuckGo handling searches: (setopt my-search-web-handler "https://duckduckgo.com/html?q=")
    • antidisestablishmentarianism
  6. With consult-omni handling searches: (setopt my-search-web-handler #'consult-omni)
    • antidisestablishmentarianism

Depending on the kind of URL, I might want to look at it in different browsers. For example, some websites like https://emacswiki.org work perfectly fine without JavaScript, so opening them in EWW (the Emacs Web Wowser) is great. Then it's right there within Emacs for easy copying, searching, etc. Some websites are a little buggy when run in anything other than Chromium. For example, MailChimp and BigBlueButton (which is the webconference server we use for EmacsConf) both behave a bit better under Google Chrome. There are some URLs I want to ignore because they don't work for me or they tend to be too paywalled, like permalink.gmane.org and medium.com. I want to open Mastodon URLs in mastodon.el. I want to open the rest of the URLs in Firefox, which is my current default browser.

To change the way Emacs opens URLs, you can customize browse-url-browser-function and browse-url-handlers. For example, to set up the behaviour I described, I can use:

(setopt browse-url-handlers
        '(("https?://?medium\\.com" . ignore)
          ("https?://[^/]+/@[^/]+/.*" . mastodon-url-lookup)
          ("https?://mailchimp\\.com" . browse-url-chrome)
          ("https?://bbb\\.emacsverse\\.org" . browse-url-chrome)
          ("https?://emacswiki.org" . eww)))
(setopt browse-url-browser-function 'browse-url-firefox)

If you wanted to use EWW as your default web browser, you could use (setopt browse-url-browser-function 'eww) instead.

Could be a fun tweak. I wonder if something like this might be handy for other people too!

View org source for this post