Thursday, July 30, 2026

Better Than Bookmarklets

The earliest web browsers didn't allow users to create custom search engines. Instead, you had to navigate to a website before searching. Or, you could use the browser's search engine to search for selected text.

That's when I started using a bookmarklet to search my public library's catalog for selected text. Before then I had to...

  • Select the text
  • Copy it to the clipboard
  • Open a new browser tab or window
  • Navigate to the library website
  • Paste the text into the search field
  • Press Enter

The bookmarklet reduced six steps to two. The process shrank to just:

  • Select text
  • Click the bookmarklet

And it preserved the clipboard.

So is the bookmarklet outdated like the dial-up modem? I think it is for most applications.

You might argue, "Bookmarklets can do more than pass selected text to a website's search tool. They can execute code."

True. For example, suppose you came across a website that announced the date of the next EmacsConf.1 How many days away is that? A bookmarklet can tell you. All you'd have to do is to select that date (December 12) and then click the bookmarklet. Of course, you'd first have to write the code, but that's the really fun part.

But my counterargument is that you don't need code. You could create a custom search engine that asks an online AI chat tool to calculate the time span with the following three-part URL:

  1. The AI chat tool's query URL2
  2. The question "How+many+days+away+is+"
  3. The search-term placeholder (usually %s)

Putting it all together gives you: https://lumo.proton.me/?q=How+many+days+away+is+%s3

To test this, I'll substitute "December 12" for %s and navigate to that URL: https://lumo.proton.me/?q=How+many+days+away+is+December%2012. Lumo inferred the missing year, chose the upcoming December 12 (2026), and calculated that it was 135 days away.

A bookmarklet could certainly do the same thing, but it would need code to infer the missing year or present multiple results. An AI already understands the ambiguity.

But what if your bookmarklet isn't provided with a date? What if it's given the name of an obscure holiday, such as "Talk Like a Pirate Day?" 4 AI can do this! Substituting "Talk Like a Pirate Day" for %s would make the URL https://lumo.proton.me/?q=How+many+days+away+is+Talk+Like+a+Pirate+Day. The response is, "Talk Like a Pirate Day is 51 days away! 🏴‍☠️" The pirate flag is a nice embellishment, isn't it?

Combined with an AI chat tool, a custom search engine can replace many bookmarklets while requiring little or no custom code.


1 https://emacsconf.org/2026/

2 I've chosen Proton's online AI tool because it's the first tool I found that submits the query immediately, without waiting for the user to press Enter or click the upload button.

3 Notice that I've replaced space characters in the question with "+" and encoded the space in the selected text with the percent-encoded value %20.

4 September 19, https://en.wikipedia.org/wiki/International_Talk_Like_a_Pirate_Day

Friday, July 24, 2026

How to Copy the Link at Point -- an Undocumented Feature in EWW

If you've ever written an Emacs function only to find out that it was there all along, masquerading as something else, this post is for you.

I just finished writing an Emacs function that copies the link at point to the kill ring in EWW. It will remain unpublished. Why? Because I discovered that shr-maybe-probe-and-copy-url (bound to u and w in eww-mode) does the same thing. Just position point on a link and invoke it.

It's not obvious that pressing w will do this. In fact the major mode help for eww-mode suggests that w will not copy the link because it's bound to eww-copy-page-url. But it turns out that if point is on a link, w will invoke shr-maybe-probe-and-copy-url, "which copies this link's URL to the kill ring." The manual goes on to say, "If point is not on a link, pressing w calls eww-copy-page-url, which will copy the current page's URL to the kill ring instead."1

Before writing my function I had been looking through the mode help (C-h m from within a EWW buffer) and the EWW source code for any function that looked like copy-link-at-point.

So how did I find out about this? I wanted to ensure that I was following the standard for capitalizing EWW, so I searched the web. This took me to the GNU Emacs manual, which has a section on EWW and shows it in all caps. But then my distractibility encouraged me to read a great deal more.

I've known that keybindings can change depending on the major mode. But it seems a keybinding can change based on context (i.e. the position of point). This idea is new to me. Maybe it's common for a function to "hand off" to another based on context. But in this case it seems irresponsible to omit documentation for the alternate behavior of a w key press. Suppose a user expects to copy the page URL and gets the URL of a link, instead?

While this was a frustrating experience, it is yet another reminder to RTFM. WDYT?

1 GNU Emacs EWW Basic Usage

Wednesday, July 22, 2026

The Problem With Tags Lists

A Tags List is an alternative to the Agenda that the user can configure with org-agenda-custom-commands. The list shows subheadings that have a certain tag. You can edit these list items with the same commands used for the item on the standard Agenda View. For example, you can invoke org-agenda-todo (bound to t) while on a tag list item to change its TODO state. However, the display doesn't always reflect the action that was performed. That can mislead the user into thinking the command had no effect.

For example, you can change an item's TODO state to DONE. When you do that for an Agenda item, usually Org Agenda removes the item from the Agenda. However, that doesn't happen to an item on a Tags List. The TODO keyword changes to DONE, but the item remains visible. However, if it's a recurring task, the state will remain at "TODO" as if nothing happened. That's because Org reverts the state back to TODO after advancing the date in accordance with the repeater.

The lack of visual feedback can lead the user to invoke the command a second time. Then the item's deadline (or schedule) will be advanced more than once, skipping the next reminder.

Here's a real world example. Suppose the TODO item is a reminder to pay the rent. It's set up with a +1m repeater so that it shows up monthly. The user pays the rent and marks it done. The deadline advances to the next month, which is fine. But nothing changes on the Tags List -- the TODO item is still there. Then the user becomes distracted1 and forgets that "Pay the Rent" was marked DONE, and so the user marks it DONE again. The deadline advances another month, so the reminder for next month's rent never appears.

A slightly better approach is to filter the Agenda View with org-agenda-tag-filter-preset to avoid generating a Tags List. But I found it tricky to use. I couldn't figure out how to use Customize to include it in one of my org-agenda-custom-commands. Instead I edited my init file to change the custom command from:

;; Dangerous!
("Bm" "Bills for the Month (Recurring)" agenda "Bills"
 ((org-agenda-span 'month) (org-agenda-files '("~/bills.org"))))

…to:

;; Less Dangerous
("Bm" "Bills for the Month (Recurring)" agenda ""
 ((org-agenda-tag-filter-preset '("+bills") nil)))

This is a better approach because when marked DONE, an item's TODO status changes to DONE even if it had a repeater, so the user has a clear indication of its status. Unfortunately, this isn't ideal because the date of the deadline doesn't advance according to the repeater; it's necessary to edit the org file manually to change DONE to TODO and then mark it DONE using org-todo2 (which enacts the repeater).

If there were such a thing as org-agenda-todo-before-hook, I'd add a call to org-agenda-check-type and abort if it returned anything other than agenda.

Another approach is to disable org-agenda-todo. I tried it. It works, but it's annoying.

My eventual solution was to modify org-agenda-todo in org-agenda.el3 so that it aborts unless the item is an agenda item. The only required change was adding (org-agenda-check-type nil 'agenda) to the begining of the function. Now when I invoke org-agenda-todo on an item in a Tags List, Emacs responds with "Not allowed in `tags'-type agenda buffer or component." Perfect!

(defun org-agenda-todo (&optional arg)
  "Cycle TODO state of line at point, also in Org file.
This changes the line at point, all other lines in the agenda referring to
the same tree node, and the headline of the tree node in the Org file."
  (interactive "P")
  (org-agenda-check-no-diary)
  (org-agenda-check-type t 'agenda) ;; add this!
  (org-agenda-maybe-loop
   #'org-agenda-todo arg nil nil
   (let* ((col (current-column))

Although this is only a one-line change, it's a good example of why I appreciate Free and Open Source Software (FOSS). Users have the freedom to choose how it operates! In a sense, we're all owners and collaborators. And even if you're not able to make the change on your own, you're part of a supportive community that will help you.

Is there something that irks you about Emacs? Have you asked for help on how to change it? Let's do that now!


1 The hypothetical user is me.

2 org-todo operates within org files; org-agenda-todo works from within the Agenda.

3 When modifying an Emacs source file, save a backup of the original and rename the corresponding .elc file. Otherwise Emacs will continue loading the compiled version, making it appear that your changes had no effect. Recompile the modified source if desired.

Tuesday, July 21, 2026

Modeling Browser History in Emacs

If there were an underappreciated web browser feature, it would be this: history.

On many browsers, history is invoked with C-h (Ctrl+h). Vivaldi shows history as a calendar. The user can choose from time intervals of Day, Week or Month and search for a keyword within that time interval.1

I tend to write about a topic after I've forgotten where I read about it. Suppose I recall reading about "nslookup" but can't remember when or where. Simple. Vivaldi remembered that I visited a page with "nslookup" on Windows Central on July 3 at 21:26. Being able to find such references helps.

History helped me track my hours when my employer required bi-weekly timesheets. I would combine browser history with Org's clock report to get a result I was actually proud to submit.

I still track my time with Org, which stores start and stop times in a drawer called LOGBOOK. But I tend to forget to start or stop the clock, so the times are unreliable. Sometimes a file's timestamp can help me reconstruct the LOGBOOK. But at times like these I'd wish that Emacs had a history feature.

Actually Emacs does have a few history-like features. One example is savehist-mode,2 which "save[s] the values of minibuffer history variables." Unfortunately, timestamps are not included.

So I configured Emacs to write a timestamp to the *Messages* buffer every time it loads or saves a file. This frees me from switching to a file manager to check a file's timestamp. Using Customize, I added functions to find-file-hook and after-save-hook:3

(find-file-hook
 '(lambda nil
     (message "%s: Loaded %s"
              (format-time-string "%Y-%m-%d %H:%M:%S" (current-time))
              (buffer-file-name))))
(after-save-hook
 '(lambda nil
    (message "%s: Saved %s"
             (format-time-string "%Y-%m-%d %H:%M:%S" (current-time))
             (buffer-file-name))))

Whenever I realize I'm not clocked in, I visit the *Messages* buffer and look for the first occurrence of something like this:

2026-07-20 12:32:33: Saved c:/Users/RayZ/AppData/Roaming/HOME/blog.org

Then I clock in and change the start time to 12:33.4

Similarly, if I've been away from the computer and realize I'm still clocked in, I'll look for the last occurrence of the message, clock out, and change the time accordingly.

The *Messages* buffer is temporary; timestamps from an earlier Emacs session are lost. But this simple system does what I need.

What methods do you use to track project time?


1 The user also can view history as a simple list in reverse chronological order. And the search can span the entire history, if desired.

2 https://doc.endlessparentheses.com/Fun/savehist-mode.html

3 Note that these expressions are arguments to custom-set-variables.

4 Timesheets required time entries in units of hours rounded to one decimal. A tenth hour is 6 minutes. So I configured Org to round clock times to 3 minutes by setting org-clock-rounding-minutes to 3. This affords more granularity. With several clock entries per heading, an even number of 0:03 clock entries would produce the desired tenth hour accuracy.

Sunday, July 19, 2026

Emacs Tip Of The Day in a Popup Frame

The Emacs commands that create a new frame disappoint me. They create a frame that shares the same set of buffers as the main frame. I don't think a Tip Of The Day (TOTD) popup should have access to all the buffers in a session. I want a popup to display some help and be easily dismissed.1

That's why I designed my Tip Of The Day to run in a separate Emacs instance. Instead of invoking the TOTD function directly, Emacs asks the operating system to launch a second Emacs process. One of the command line switches loads the Lisp file in which the TOTD function is defined, and another runs the function. This is achieved by invoking call-process-shell-command to run a one-line batch file2 that contains this:

"path_to_emacs\emacs.exe" -Q -g 80x42-2+2 -l totd.el --eval (ztotd)

The -l switch loads totd.el, defining both totd and ztotd. The --eval switch then invokes ztotd. (The other function, totd is not used; it's retained for reference.) The -Q switch starts Emacs "quietly." -g defines window geometries:3 WxH+X+Y, where

  • W and H specify the frame's width and height in character units.
  • X and Y, if ≥0, specify the pixel coordinates of the upper left corner of the window. If <0, specify the distance from the right and bottom of the screen.

This is the statement in my init file that runs the batch file:

(call-process-shell-command "cmd.exe /Q /D /C invoke-emacs-totd.bat" nil 0)

Here's the listing for totd.el. The first function, totd, is based on code that Dave Pearson posted on EmacsWiki.4 To make it work with modern Emacs, I replaced (require 'cl) with (require 'cl-lib) and (loop for s...) with (cl-loop for s...).

;; Time-stamp: "2026-07-19 17:00:17 RayZ"
;; totd.el by DavePearson
;; This small code snippet displays a “tip of the day”
;;
;; 2026-07-14 Downloaded from https://www.emacswiki.org/emacs/TipOfTheDay
;; 2026-07-19 Derive ztotd to run in separate session

(require 'cl-lib)

(defun totd ()
 (interactive)
 (with-output-to-temp-buffer "*Tip of the day*"
   (let* ((commands (cl-loop for s being the symbols
                          when (commandp s) collect s))
          (command (nth (random (length commands)) commands)))
     (princ
      (concat "Your tip for the day is:\n========================\n\n"
              (describe-function command)
              "\n\nInvoke with:\n\n"
              (with-temp-buffer
                (where-is command t)
                (buffer-string)))))))

(defun ztotd ()
  "Display documentation of a random Emacs function to provide
a Tip Of The Day.  It is intended to run in a separate
session of Emacs where it modifies the frame to resemble a
minimalist popup.  Invoke at the command line with (for example):

  emacs.exe -Q -g 80x42-2+2 -l totd.el --eval (ztotd)"
 (interactive)
 (set-buffer (generate-new-buffer "Tip of the day"))
   (let* ((commands (cl-loop for s being the symbols
                          when (commandp s) collect s))
          (command (nth (random (length commands)) commands)))
     (insert
      (concat (describe-function command)
              "\n\nInvoke with:\n\n"
              (with-temp-buffer
                (where-is command t)
                (buffer-string))))
     (tool-bar-mode 0)
     (menu-bar-mode 0)
     (setq frame-title-format '("Gnu Emacs Tip of the day"))
     (pop-to-buffer (current-buffer))
     (goto-char (point-min))
     (delete-other-windows)
     (message "Press C-x C-c to dismiss")))

totd is very intrusive by design -- it's supposed to put a help page "in your face." But I wanted a "clean" look. So I eliminated the toolbar and menu from the frame, moved "Tip of the Day" from the buffer to the title, and got rid of other windows. Plus I call it with call-process-shell-command instead of shell-command to prevent a shell output window from appearing in the main frame.5

The batch file allows the OS itself to initiate the display of TOTD. It can be set up to run right after logon, or combined with Tea Timer or Scheduler for a periodic display.

I like how this performs. However, I hardly ever close and restart Emacs. As a result, I may go a week or two without seeing a tip. Is that why Emacs has never included a built-in Tip Of The Day? Or is it just so easy to implement that "the proof has been left as an exercise to the user?" Or maybe the help that's shown is too specific. For example, "What does image-dired-delete-tag actually do?" you might wonder. I think that's the whole point of a TOTD -- to inspire wonder!

If you try this on a non-Windows system, please let me know whether it works.


1 I tried display-buffer-pop-up-frame. Somehow the popup lost focus and got buried behind the main frame. When I closed the main frame, I thought that all my files were saved and Emacs was closed. Only then did I realize the popup was still running. When I tried to close it, Emacs warned about open files. So nervously I maximized the small popup frame to review the unsaved buffers.

2 In fact the batch file contains several lines of comments, too, as well as the cherished @echo off statement at the very beginning. @echo off prevents the many lines of comments from getting dumped to standard output; otherwise those comments would end up in a shell buffer.

3 For more information on command line switches for Emacs, see: https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Invocation.html

4 Please see https://www.emacswiki.org/emacs/TipOfTheDay

5 Thanks to Jackson Ray Hamilton for this suggestion. https://stackoverflow.com/a/22982525

Friday, July 10, 2026

One Hundred Times the Highest Priority

Please note that org‑get‑cust‑priority has been updated. The code that was published on 2026-07-10 causes org‑tags‑list to crash.

In the previous post,1 I was disappointed to learn that valid priority characters are limited to 0–9 and A–Z. I thought it would be neat if the exclamation mark ("!") could represent the ultimate priority, a sort of "drop everything and do this immediately" kind of priority.

It turns out that you can use "!" for this purpose, regardless of which priority characters you've configured. The trick is to write a function and assign it to org-priority-get-priority-function.

The function (which I named org-get-cust-priority) simply looks for "!" in the priority cookie and returns one hundred times the value that would be returned by the highest valid priority. If it doesn't find "!" it simply calls a modified version of the usual org-get-property function,2 which I named org-get-std-priority. The two functions are shown below.3

(defun org-get-cust-priority (s)
  "Return 100 times the highest priority when S contains
a priority cookie with `!'.  Otherwise call the usual
`org-get-property' function. Intended to be the function
`org-priority-get-priority-function' is set to."
  (interactive)
  (if (not (functionp org-priority-get-priority-function))
      (org-get-std-priority s)
    (string-match ".*?\\(\\[#\\(!\\)\\] ?\\)" s)
    (if (and (string-match ".*?\\(\\[#\\(!\\)\\] ?\\)" s) (string-equal (match-string 2 s) "!"))
        (* 100000 (abs (- org-priority-lowest org-priority-highest)))
      (org-get-std-priority s))))

(defun org-get-std-priority (s)
  "Find priority cookie and return priority.
S is a string against which you can match `org-priority-regexp'.
Same function as `org-get-priority' sans test for a custom
function in `org-priority-get-priority-function'."
  (save-match-data
    (if (not (string-match org-priority-regexp s))
        (* 1000 (- org-priority-lowest org-priority-default))
      (* 1000 (- org-priority-lowest
                 (org-priority-to-value (match-string 2 s)))))))

The number that Org uses to sort on priority depends on the values of org-priority-lowest and org-priority-highest,4 which are "C" and "A" by default. In the default configuration, the highest priority has a numeric value of 2000, while the lowest has a value of 0. If the highest and lowest priority characters are 1 and 9, respectively, the highest priority has a value of 8000.

You know you need this if you have about a dozen items on your agenda and half of them are prioritized A.


2026-08-13 Update org-get-cust-priority. Previous version breaks org-tags-list.

1 https://ray-on-emacs.blogspot.com/2026/07/numeric-priorities-in-org-mode.html
2 Modified just to prevent recursion.
3 I know this code isn't particuarly elegant. I could have made custom variables for "!" and the priority multiplier, for example. I'd like to use it for a while before deciding whether it's worth investing any more effort. I do welcome suggestions on how to improve.
4 Or rather, the difference between the highest and lowest priorities.

Thursday, July 9, 2026

Numeric Priorities in Org Mode

Org mode is configured "out-of-the-box" with these three levels of priority: A, B, C, where A is the highest priority. However, this can be changed. You can use numbers to indicate priority, such as 1 (highest) to 10 (lowest) as shown in the documentation.1

If you specify priorities from 65 to 90, Org will interpret the numbers as ordinal values of ASCII characters and give you "A" to "Z." If you try to be clever and specify priorities as 65 to 91 (91 is the decimal ASCII code for left square bracket character) you'll be disappointed that when you lower the priority from "Z," you don't get "[." (I was disappointed, anyway.)

You can define priorities from 10 to 99, for example. But if you try to get to a priority higher than 10 using org-priority-up, Org will clear the priority cookie. However, you can manually enter a priority of 0 to ensure the item appears above all other items. (Please see Figure 1, below.) Thus, a priority value of 0 can be your "stat" item. It would be super neat if Org could understand that ASCII characters below 0 (such as the exclamation mark) are an even higher priority than 0. Unfortunately, it doesn't even recognize [#!] as a priority cookie.

A large priority span seems like it would be useful. For example, you might have all utility bills set from 50 to 59, and shopping for birthday or anniversary gifts from 10 to 19. However, in order to go from a priority of 50 to 10, you'd have to press the Up Arrow key forty times. If you think you can invoke M-4 0 M-x org-priority-up to go quickly from 50 to 10, you'll be disappointed (again). The function org-priority-up doesn't accept a prefix argument.2

How do you use priority in Org?


1 Org Manual -- Priorities

2 But you can record a macro that consists of a single S-<up> key press and then play it back with a prefix argument of 39.

Wednesday, July 8, 2026

Configure Web Browser to be More Emacs-like

Emacs, LibreOffice Writer and Vivaldi are the productivity tools I use most often. So I like to incorporate a feature from one into the others. One example is the ability to initiate a search from selected text. I added this feature to LibreOffice as described here. Another nice feature is the ability to switch between the current buffer and the next (or previous) buffer with C-<TAB> and C-S-<TAB> that I added to Emacs1.

But I've also configured Vivaldi with a useful Emacs feature. Now it responds to C-S-k by invoking "Close tabs to the Right." Emacs devotees know that C-k is bound to kill-line, which "Kill[s] the rest of the current line...."2 If you imagine that the row of tabs is a line of text, it's natural to expect C-k (or C-S-k) would remove all the tabs to the right of the current tab.

Why did I choose the key combination C-S-k instead of C-k? I think at the time C-k performed another function. Or perhaps I felt that it was more appropriate to use two modifier keys for such a significant operation. I rarely need to delete tabs to the left; if I did, I'd bind C-M-S-k to "Close tabs to the Left."

You might utter the following complaint, "But C-S-k doesn't work for Gmail; it brings up a virtual keyboard instead!" Correct. So while we're on the topic of Vivaldi key configurations, I'd like to point out that it's possible to tell Vivaldi to ignore a website's key assignment, allowing the key to pass to Vivaldi. Just search for the setting "Browser Priority Shortcuts," click on one of the existing key combinations and press C-S-k to add it to the others.

I rely on the web browser's tab history a great deal, too. I'm working on implementing something vaguely similar for Emacs."


1 To get the C-<TAB> and C-S-<TAB> to switch buffers, just add this to your init file:

  (keymap-global-set "C-<tab>" 'next-buffer)
  (keymap-global-set "C-S-<tab>" 'previous-buffer)

2 https://www.gnu.org/software/emacs/manual/html_node/emacs/Killing-by-Lines.html

Monday, July 6, 2026

Comment on Org Source Blocks

Yesterday I wrote that I prefer putting each code snippet into its own dedicated file rather than into an org source block because "I don't want the code to execute with org-mode active."

What was I thinking; what does it mean?

Here's a scenario. I'm developing functions to create a document in some markup language. The colorizer function applies a color tag to the first word in a given string.

It works great in my Org source block. But when I add it to the lisp file and invoke it, it doesn't do exactly what I expect.

In Org mode, the single quote is a word constituent, which is fine for my purpose. However, in emacs-lisp-mode, single quote is a separator. The effect is demonstrated below. Note the code in the testre.el file tags only "Sophia," not "Sophia's," while the source block applies the tag as intended.

(defun colorizer (s)
  "Add blue color tag to first word in S."
  (interactive)
  (replace-regexp-in-string "\\(\\w+\\)\\(.+\\)" "blue{\\1}\\2" s t))

(defun testre ()
  "Test colorizer."
  (interactive)
  (message
   (concat
    (colorizer "Blanche lives in Miami.")
    " "
    (colorizer "Sofia's sweater is yellow."))))

(testre)

#+RESULTS:
: blue{Blanche} lives in Miami. blue{Sofia’s} sweater is yellow.

Here's what the code in the testre.el file produces:

  blue{Blanche} lives in Miami. blue{Sofia}’s sweater is yellow.

Another problem with having Org mode active in a lisp source block is that indenting doesn't work as expected. Pressing C-M-\ does nothing (if I'm lucky) or something undesirable. Once I activate emacs-lisp-mode, C-M-\ makes the code look nice again. (If I re-activate Org mode the buffer reloads, and I lose my place in the document.) This makes sense when you recall that C-M-\ is bound to indent-according-to-mode.

I'm sure I'm doing something wrong, or I have something misconfigured.

Sunday, July 5, 2026

Emacs Carnival July 2026 -- Programming

I don't think of Emacs as an IDE or even a programming environment unless I'm writing Lisp to automate Emacs. Instead, I consider Emacs to be a text editor with some useful extras.

Syntax highlighting (font-lock-mode) is a nice extra. It might seem strange that I consider syntax highlighting to be an extra. If you've used a text editor 40 years ago, you'll understand why -- text editors didn't have the feature. Syntax highlighting was the purview of the compiler tool. My first experience with syntax highlighting was with Borland Turbo Pascal; I used Borland Turbo C soon after. (I think Borland's Turbo Assembler, TASM, had the feature, too.)

According to packages-selected-packages, these are the programming modes I have configured:

  • basic-mode
  • csv-mode
  • gnuplot
  • matlab-mode
  • powershell
  • python and python-mode
  • Swift mode

I don't program in Swift. Perhaps the package is a dependency for another mode. Or maybe I was ambitious one day and thought I'd try another programming language.

Is csv a programming language? If you automate the generation of spreadsheets. it's nice to have some help. But to be honest, I've not used it very often.

Easy access to shell is another nice extra. I have two functions to make use of it when I write batch files. They execute either the current line (exeln) or the entire buffer (shellfn) with some sort of shell command; they are shown below. The Windows binary uses cmdproxy.exe from MinGW -- it works out-of-the-box. But I use the Windows start program for shellfn because it invokes the correct executable based on the file's extension. Thus, Python runs a .py file; cmd, a .bat; Octave, a .m file.

;; This is bound to C-c x
;; Many thanks to Sebastián Monía for suggesting many improvements
(defun exeln (arg)
  "Execute  current line as a shell command.
With prefix ARG, run asynchronously."
  (interactive "P")
  (funcall (if arg
               #'async-shell-command
             #'shell-command)
           (thing-at-point 'line t)))

;; This is bound to C-c s
(defun shellfn ()
  "Invokes the shell using the current buffer file name as a parameter."
  (interactive)
  (shell-command (concat "start " (buffer-file-name))))

I've tried to use source blocks in Org, but I prefer putting each code snippet into its own dedicated file. Why? One reason is because I don't want the code to execute with org-mode active. Another reason is that indentation doesn't work properly. Switching modes fixes both issues, but it gets annoying very quickly. This is a topic for another post.

I look forward to seeing how other folks use Emacs for programming.

Friday, July 3, 2026

Circumvent LibreOffice Macros with Templates

Instead of writing LibreOffice macros to change a default behavior, consider customizing the default template.

Creating macros is fun for me. But I agree, automating LibreOffice with macros is daunting, and the recorder is a poor way to begin writing code.1

Recently I wanted to change the default anchor position of an image. When I insert an image by pasting, I want its anchor is to be "As Character" as opposed to paragraph, which was the current default.

I tried to record the process:

  • Paste
  • F4 (properties)
  • As Character
  • OK
  • Press Enter

The recorder captured the Paste and Enter keys well. Unfortunately, it choked on the macro's primary element -- applying the As Character property.

But then I wondered, "Can the default anchor position be set to "As Character" in Options?" "No, it cannot," I answered.2 "But you can change it in the default template. Here's how:"

  • Start LibreOffice Writer. This should present a new, blank document. But if it doesn't, press Ctrl+N.
  • Press Alt+2 to open the Styles sidebar.
  • Click the Frame Styles icon. (Please see Figure 1, Frame Styles below.)
  • An image of the sidebar in which Styles is selected.  The Frame Styles button has been clicked, and the mouse pointer hovers over on the icon so that the "Frame Styles" tooltip shows.
    Figure 1 -- Frame Styles

  • Right click on Graphics and choose Edit Style... as shown in Figure 2.
  • Image shows how the interface appears after right-clicking the Graphics style element.  The context menu shows three choices: New, Edit Style and Hide
    Figure 2 -- Edit Graphics Style

  • Choose "Type" on the left. Its icon looks an anchor.
  • Under Anchor, click the radio button next to As character.
  • Review the other settings and change whatever other defaults that don't make sense to you. I recommend clicking the checkbox to lock Height to Width (aka Aspect Ratio in other applications). Mine was unchecked, and I always found myself clicking it.
  • Choose File | Templates | Save as Template as shown in Figure 3. Note this is different from doing File | Save As and then choosing the OTT file type. And yes, you will be saving the image in the template, but it can be removed later.
  • The image shows the three options after clicking File and then Templates from the menu
    Figure 3 -- Save as Template Menu Selection

  • Enter a name for the template. I entered DEMO for this How-To, but I tend to give my templates more description names that include the paper size, such as "4 x 6 index card" or "5 x 7 greeting card LS."
  • Choose a category to enable the Save button.
  • Click the checkbox labeled "Set as default template" to ensure that every subsequent new Writer document will be derived from the new template.
  • Note that these three choices (Name, Category, Default) can be changed in the Template Manager (Ctrl+Shift+N). The dialog should look similar to the one in Figure 4, below.

  • The image is a screenshot of the Save Template Dialog box, where the template name is "DEMO," the category "My Templates" is selected and the checkbox indicates that it should be set as the default template
    Figure 4 -- The Save Template Dialog Box


  • Press Ctrl+N to create a new document. Paste an image into it to test the new template. Hopefully3 you'll get the desired result.

1 "When the macro recorder is not able to solve a specific problem, the usual solution is to write code using the OpenOffice.org objects. Unfortunately, there is a steep learning curve for the OOo objects."
2 Sometimes I gain insight into a problem by splitting myself into student and teacher.
3 I write "Hopefully" because as I repeated these steps to test them, I managed to obliterate my own default template. And I couldn't seem to fix it without restarting Writer.