<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://planet.emacslife.com/</id>
    <title>Planet Emacslife</title>
    <updated>2026-09-07T12:32:00.921Z</updated>
    <generator>https://github.com/gap-hub/feed</generator>
    <author>
        <name>Various authors</name>
    </author>
    <link rel="alternate" href="https://planet.emacslife.com/"/>
    <link rel="self" href="https://planet.emacslife.com/atom.xml"/>
    <rights>Various authors</rights>
    <entry>
        <title type="html"><![CDATA[James Dyer: Git Worktreess Without Leaving Built-in VC]]></title>
        <id>https://emacs.dyerdwelling.family/emacs/20260905090959-emacs--git-worktrees-and-branch-surgery-in-built-in-vc/</id>
        <link href="https://emacs.dyerdwelling.family/emacs/20260905090959-emacs--git-worktrees-and-branch-surgery-in-built-in-vc/"/>
        <updated>2026-09-07T12:31:30.848Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
More <code>vc</code> stuff, and this week's rabbit hole was git worktrees (Bozhidar Batsov's <a href="https://emacsredux.com/blog/2026/09/02/working-with-git-worktrees-in-magit/">Working with Git Worktrees in Magit</a> is what sent me down this particular rabbit hole!).  It was an itch that manifested itself while reading this blog and mainly seeing that worktrees seemed to make a good deal of sense to me (especially as I am quite a subversion veteran). I have always felt uncomfortable with the default git in-situ branch switching and prefer to branch develop away from the mainline in a physical separate directory and this is just what worktrees facilitate.  So how to do this with built-in vc-mode tools?, well as always this requires a little work and yes, I could use Magit, but I'm seeing how far I can push the built in vc.
</p>


<div class="figure">
<p><img loading="lazy" src="https://emacs.dyerdwelling.family/static/emacs/20260905090959-emacs--Git-Worktrees-and-Branch-Surgery-in-Built-in-VC.jpg" alt="20260905090959-emacs--Git-Worktrees-and-Branch-Surgery-in-Built-in-VC.jpg" width="100%">
</p>
</div>


<p>
The funny thing is that <code>vc</code> already understands worktrees perfectly well: a worktree is just a directory holding a <code>.git</code> pointer file instead of a <code>.git</code> folder, <code>vc-git</code> resolves that transparently, and diffs, commits and <code>vc-dir</code> all quietly do the right thing per worktree. What <code>vc</code> cannot do is actually seemingly <i>manage</i> them. There is no command to create one, switch between them, move one, list them, or throw one away, and the branch side is nearly as thin. Magit does all of this beautifully, of course - but I am stubborn about staying inside core Emacs where I can, so I spent an evening teaching <code>vc-dir</code> some new tricks.  And yes, I do understand that vc in Emacs is in fact a generic abstraction and works well across all different types of version control, hence the reduction to a core subset of general source code commands, with a few extra added on of course for those obvious missing pieces, but I want to add the less obvious pieces, and certainly for git, worktrees is probably going to be one of them.  Right lets get on with this and write some code!
</p>

<p>
Everything funnels through one small worker that shells out via <code>vc-git-command</code> and then drops you straight into <code>vc-dir</code> in the new tree:
</p>

<p>
Here is what I implemented:
</p>

<div class="org-src-container">
<pre><span>;;</span><span>
</span><span>;; </span><span>-&gt; vc-git-worktree-core
</span><span>;;</span><span>
</span>(<span>defun</span> <span>my/vc-git-branches</span> ()
  <span>"Return a list of local branch names in the current git repository."</span>
  (<span>let</span> ((root (vc-git-root default-directory)))
    (<span>when</span> root
      (<span>with-temp-buffer</span>
        (<span>let</span> ((default-directory root))
          (vc-git-command (current-buffer) 0 nil
                          <span>"branch"</span> <span>"--format=%(refname:short)"</span>))
        (split-string (buffer-string) <span>"\n"</span> t)))))<br>
(<span>defun</span> <span>my/vc-git--worktree-root</span> ()
  <span>"Return the git worktree root for `</span><span>default-directory</span><span>'.
Signal a `</span><span>user-error</span><span>' when not inside a Git repository."</span>
  (<span>or</span> (vc-git-root default-directory)
      (<span>user-error</span> <span>"Not in a Git repository"</span>)))<br>
(<span>defun</span> <span>my/vc-git-worktree-entries</span> ()
  <span>"Return the worktrees of the current repository as a list of plists.
Each plist has keys :path, :head, :branch, :detached, :bare,
:locked and :prunable, parsed from `git worktree list --porcelain'."</span>
  (<span>let</span> ((root (my/vc-git--worktree-root))
        entries current)
    (<span>with-temp-buffer</span>
      (<span>let</span> ((default-directory root))
        (vc-git-command (current-buffer) 0 nil
                        <span>"worktree"</span> <span>"list"</span> <span>"--porcelain"</span>))
      (goto-char (point-min))
      (<span>while</span> (not (eobp))
        (<span>let</span> ((line (buffer-substring-no-properties
                     (line-beginning-position) (line-end-position))))
          (<span>cond</span>
           ((string-prefix-p <span>"worktree "</span> line)
            (<span>when</span> current (<span>push</span> current entries))
            (<span>setq</span> current (list <span>:path</span> (substring line 9))))
           ((string-prefix-p <span>"HEAD "</span> line)
            (<span>setq</span> current (plist-put current <span>:head</span> (substring line 5))))
           ((string-prefix-p <span>"branch "</span> line)
            (<span>let</span> ((ref (substring line 7)))
              (<span>setq</span> current
                    (plist-put current <span>:branch</span>
                               (<span>if</span> (string-prefix-p <span>"refs/heads/"</span> ref)
                                   (substring ref 11)
                                 ref)))))
           ((string= line <span>"detached"</span>)
            (<span>setq</span> current (plist-put current <span>:detached</span> t)))
           ((string= line <span>"bare"</span>)
            (<span>setq</span> current (plist-put current <span>:bare</span> t)))
           ((string-prefix-p <span>"locked"</span> line)
            (<span>setq</span> current
                  (plist-put current <span>:locked</span>
                               (<span>if</span> (&gt; (length line) 6)
                                   (substring line 7)
                                 t))))
           ((string-prefix-p <span>"prunable"</span> line)
            (<span>setq</span> current
                  (plist-put current <span>:prunable</span>
                               (<span>if</span> (&gt; (length line) 8)
                                   (substring line 9)
                                 t))))))
        (forward-line 1)))
    (<span>when</span> current (<span>push</span> current entries))
    (nreverse entries)))<br>
(<span>defun</span> <span>my/vc-git--default-worktree-path</span> (root branch)
  <span>"Return a default new-worktree path for BRANCH next to ROOT.
The default is a sibling directory of ROOT prefixed with the project
name, e.g. &lt;parent&gt;/myproj-feature for branch feature of myproj.
Slashes in BRANCH become dashes so hierarchical branches cannot
collide; an empty BRANCH falls back to &lt;project&gt;-worktree."</span>
  (<span>let</span> ((project (file-name-nondirectory
                  (directory-file-name (expand-file-name root)))))
    (expand-file-name
     (concat project <span>"-"</span>
             (<span>if</span> (string-empty-p branch)
                 <span>"worktree"</span>
               (replace-regexp-in-string <span>"/"</span> <span>"-"</span> branch)))
     (file-name-directory (directory-file-name (expand-file-name root))))))<br>
(<span>defun</span> <span>my/vc-git-worktree-add</span> (path branch <span>&amp;optional</span> new-branch start-point)
  <span>"Create a Git worktree at PATH checking out BRANCH, then open `</span><span>vc-dir</span><span>'.
An empty BRANCH checks out HEAD.  With prefix argument NEW-BRANCH,
create BRANCH as a new branch from START-POINT (defaulting to HEAD)
instead of checking out an existing branch or revision."</span>
  (<span>interactive</span>
   (<span>let*</span> ((root (my/vc-git--worktree-root))
          (branches (<span>or</span> (my/vc-git-branches) '()))
          (make-new (<span>if</span> current-prefix-arg t nil))
          (branch (<span>if</span> make-new
                      (read-string <span>"New branch name: "</span>)
                    (completing-read <span>"Checkout branch (empty = HEAD): "</span>
                                     branches nil nil)))
          (default-path (my/vc-git--default-worktree-path root branch))
          (path (read-file-name <span>"Worktree location: "</span>
                                (file-name-directory default-path)
                                nil nil
                                (file-name-nondirectory default-path)))
          (start (<span>and</span> make-new
                      (completing-read <span>"Start point: "</span>
                                       (delete-dups
                                        (append (list <span>"HEAD"</span>) branches
                                                (my/vc-git-tags)))
                                       nil nil nil nil <span>"HEAD"</span>))))
     (list (expand-file-name path) branch make-new start)))
  (my/vc-git-worktree--create path branch new-branch start-point))<br>
(<span>defun</span> <span>my/vc-git-worktree--create</span> (path branch new-branch start-point)
  <span>"Create a Git worktree at PATH and open `</span><span>vc-dir</span><span>' there.
BRANCH names an existing branch or revision, unless NEW-BRANCH is
non-nil, in which case BRANCH is created from START-POINT first."</span>
  (<span>let</span> ((root (my/vc-git--worktree-root))
        (expanded (expand-file-name path)))
    (<span>let</span> ((default-directory root))
      (<span>cond</span>
       (new-branch
        (<span>when</span> (string-empty-p branch)
          (<span>user-error</span> <span>"New branch name must not be empty"</span>))
        (vc-git-command nil 0 nil <span>"worktree"</span> <span>"add"</span> <span>"-b"</span> branch expanded
                        (<span>if</span> (string-empty-p start-point) <span>"HEAD"</span> start-point)))
       ((string-empty-p branch)
        (vc-git-command nil 0 nil <span>"worktree"</span> <span>"add"</span> expanded))
       (t
        (vc-git-command nil 0 nil <span>"worktree"</span> <span>"add"</span> expanded branch))))
    (message <span>"Created worktree %s"</span> expanded)
    (vc-dir expanded)))<br>
(<span>defun</span> <span>my/vc-git-worktree-checkout</span> (path branch)
  <span>"Check out existing BRANCH in a new worktree at PATH, then open `</span><span>vc-dir</span><span>'.
The Magit-style `</span><span>magit-worktree-checkout</span><span>' first action: the location
defaults to a sibling directory named &lt;project&gt;-&lt;branch&gt;."</span>
  (<span>interactive</span>
   (<span>let*</span> ((root (my/vc-git--worktree-root))
          (branches (<span>or</span> (my/vc-git-branches) '()))
          (branch (completing-read <span>"Checkout branch in new worktree: "</span>
                                   branches nil nil))
          (default-path (my/vc-git--default-worktree-path root branch))
          (path (read-file-name <span>"Worktree location: "</span>
                                (file-name-directory default-path)
                                nil nil
                                (file-name-nondirectory default-path))))
     (list (expand-file-name path) branch)))
  (<span>when</span> (string-empty-p branch)
    (<span>user-error</span> <span>"Branch must not be empty"</span>))
  (my/vc-git-worktree--create path branch nil nil))<br>
(<span>defun</span> <span>my/vc-git-worktree-branch</span> (path branch start-point)
  <span>"Create new BRANCH from START-POINT in a new worktree at PATH.
Then open `</span><span>vc-dir</span><span>'.  The Magit-style `</span><span>magit-worktree-branch</span><span>' first
action: the location defaults to a sibling directory named &lt;project&gt;-&lt;branch&gt;."</span>
  (<span>interactive</span>
   (<span>let*</span> ((root (my/vc-git--worktree-root))
          (branches (<span>or</span> (my/vc-git-branches) '()))
          (branch (read-string <span>"New branch name: "</span>))
          (start (completing-read <span>"Start point: "</span>
                                  (delete-dups
                                   (append (list <span>"HEAD"</span>) branches
                                           (my/vc-git-tags)))
                                  nil nil nil nil <span>"HEAD"</span>))
          (default-path (my/vc-git--default-worktree-path root branch))
          (path (read-file-name <span>"Worktree location: "</span>
                                (file-name-directory default-path)
                                nil nil
                                (file-name-nondirectory default-path))))
     (list (expand-file-name path) branch start)))
  (my/vc-git-worktree--create path branch t start-point))<br>
(<span>defun</span> <span>my/vc-git-worktree-move</span> (path new-path)
  <span>"Move the Git worktree at PATH to NEW-PATH (`git worktree move').
Interactively, prompt for a worktree, defaulting to the current one."</span>
  (<span>interactive</span>
   (<span>let*</span> ((entries (my/vc-git-worktree-entries))
          (paths (mapcar (<span>lambda</span> (e) (plist-get e <span>:path</span>)) entries))
          (current (my/vc-git--worktree-root)))
     (<span>unless</span> paths (<span>user-error</span> <span>"No Git worktrees found"</span>))
     (<span>let</span> ((path (completing-read <span>"Move worktree: "</span> paths
                                  nil t nil nil current)))
       (list path (read-file-name
                   <span>"Move to: "</span>
                   (file-name-directory
                    (directory-file-name (expand-file-name path)))
                   nil nil)))))
  (<span>let*</span> ((root (my/vc-git--worktree-root))
         (expanded (expand-file-name path))
         (target (expand-file-name new-path)))
    (<span>let</span> ((default-directory root))
      (vc-git-command nil 0 nil <span>"worktree"</span> <span>"move"</span> expanded target))
    (message <span>"Moved worktree %s to %s"</span> expanded target)
    (<span>when</span> (derived-mode-p 'vc-dir-mode) (vc-dir-refresh))))<br>
(<span>defun</span> <span>my/vc-git-worktree-switch</span> (path)
  <span>"Interactively select an existing Git worktree and open `</span><span>vc-dir</span><span>' in it."</span>
  (<span>interactive</span>
   (<span>let*</span> ((entries (my/vc-git-worktree-entries))
          (table (mapcar (<span>lambda</span> (e)
                           (cons (plist-get e <span>:path</span>) e))
                         entries))
          (current (my/vc-git--worktree-root)))
     (<span>unless</span> table (<span>user-error</span> <span>"No Git worktrees found"</span>))
     (<span>let</span> ((completion-extra-properties
            (list <span>:annotation-function</span>
                  (<span>lambda</span> (cand)
                    (<span>let</span> ((e (cdr (assoc cand table))))
                      (<span>when</span> e
                        (concat <span>"  ["</span>
                                (<span>or</span> (plist-get e <span>:branch</span>)
                                    (<span>and</span> (plist-get e <span>:detached</span>)
                                         <span>"detached"</span>)
                                    <span>"?"</span>)
                                (<span>when</span> (plist-get e <span>:bare</span>) <span>", bare"</span>)
                                <span>"]"</span>)))))))
       (list (completing-read <span>"Switch to worktree: "</span> table
                              nil t nil nil current)))))
  (vc-dir path))<br>
(<span>defun</span> <span>my/vc-git-worktree-remove</span> (path <span>&amp;optional</span> force)
  <span>"Remove the Git worktree at PATH, prompting first for confirmation.
With prefix argument FORCE, pass --force to git.
Interactively, prompt for a worktree, defaulting to the current one."</span>
  (<span>interactive</span>
   (<span>let*</span> ((entries (my/vc-git-worktree-entries))
          (paths (mapcar (<span>lambda</span> (e) (plist-get e <span>:path</span>)) entries))
          (current (my/vc-git--worktree-root)))
     (<span>unless</span> paths (<span>user-error</span> <span>"No Git worktrees found"</span>))
     (list (completing-read <span>"Remove worktree: "</span> paths
                            nil t nil nil current)
           (<span>if</span> current-prefix-arg t nil))))
  (<span>let*</span> ((root (my/vc-git--worktree-root))
         (expanded (expand-file-name path))
         (main-root (plist-get (car (my/vc-git-worktree-entries)) <span>:path</span>))
         (here (file-truename default-directory))
         (gone (file-truename expanded)))
    (<span>when</span> (yes-or-no-p (format <span>"Remove worktree %s%s? "</span>
                               expanded (<span>if</span> force <span>" (forced)"</span> <span>""</span>)))
      (<span>let</span> ((default-directory root))
        (<span>if</span> force
            (vc-git-command nil 0 nil <span>"worktree"</span> <span>"remove"</span> <span>"--force"</span> expanded)
          (vc-git-command nil 0 nil <span>"worktree"</span> <span>"remove"</span> expanded)))
      (message <span>"Removed worktree %s"</span> expanded)
      (<span>when</span> (derived-mode-p 'vc-dir-mode)
        (<span>if</span> (string-prefix-p gone here)
            (vc-dir (<span>or</span> main-root root))
          (vc-dir-refresh))))))<br>
(<span>defun</span> <span>my/vc-git-worktree-prune</span> ()
  <span>"Prune stale Git worktree metadata (`git worktree prune')."</span>
  (<span>interactive</span>)
  (<span>let</span> ((root (my/vc-git--worktree-root)))
    (<span>let</span> ((default-directory root))
      (vc-git-command nil 0 nil <span>"worktree"</span> <span>"prune"</span>))
    (message <span>"Pruned worktrees in %s"</span> root)
    (<span>when</span> (derived-mode-p 'vc-dir-mode) (vc-dir-refresh))))<br>
(<span>defvar-local</span> <span>my/vc-git-worktree-list--root</span> nil
  <span>"Repository root shown in the current worktree list buffer."</span>)<br>
(<span>defvar</span> <span>my/vc-git-worktree-list-mode-map</span>
  (<span>let</span> ((map (make-sparse-keymap)))
    (define-key map (kbd <span>"RET"</span>) #'my/vc-git-worktree-list-visit)
    (define-key map (kbd <span>"g"</span>) #'my/vc-git-worktree-list)
    (define-key map (kbd <span>"q"</span>) #'quit-window)
    map)
  <span>"Keymap for `</span><span>my/vc-git-worktree-list-mode</span><span>'."</span>)<br>
(<span>define-derived-mode</span> <span>my/vc-git-worktree-list-mode</span> special-mode <span>"Worktrees"</span>
  <span>"Major mode for listing Git worktrees.
\\{</span><span>my/vc-git-worktree-list-mode-map</span><span>}"</span>)<br>
(<span>defun</span> <span>my/vc-git-worktree-list-visit</span> ()
  <span>"Open `</span><span>vc-dir</span><span>' for the worktree on the current line."</span>
  (<span>interactive</span>)
  (<span>let*</span> ((here (line-number-at-pos))
         (btn (<span>or</span> (button-at (point))
                  (<span>save-excursion</span>
                    (beginning-of-line)
                    (next-button (point) t))))
         (ok (<span>and</span> btn (= here (line-number-at-pos (button-start btn))))))
    (<span>if</span> ok
        (vc-dir (button-label btn))
      (<span>user-error</span> <span>"No worktree on this line"</span>))))<br>
(<span>defun</span> <span>my/vc-git-worktree-list</span> ()
  <span>"List Git worktrees of the current repository in a dedicated buffer.
RET on a path opens `</span><span>vc-dir</span><span>' there; `</span><span>g</span><span>' refreshes the list, `</span><span>q</span><span>' quits."</span>
  (<span>interactive</span>)
  (<span>let*</span> ((root (<span>if</span> (derived-mode-p 'my/vc-git-worktree-list-mode)
                   (<span>or</span> my/vc-git-worktree-list--root
                       (my/vc-git--worktree-root))
                 (my/vc-git--worktree-root)))
         (default-directory root)
         (entries (my/vc-git-worktree-entries)))
    (<span>unless</span> entries (<span>user-error</span> <span>"No Git worktrees found"</span>))
    (<span>with-current-buffer</span> (get-buffer-create <span>"*vc-git worktrees*"</span>)
      (<span>let</span> ((inhibit-read-only t))
        (erase-buffer)
        (my/vc-git-worktree-list-mode)
        (<span>setq</span> my/vc-git-worktree-list--root root)
        (<span>setq</span> default-directory root)
        (insert (format <span>"Worktrees for %s\n\n"</span> root))
        (<span>dolist</span> (e entries)
          (<span>let*</span> ((path (plist-get e <span>:path</span>))
                 (head (<span>or</span> (plist-get e <span>:head</span>) <span>""</span>))
                 (short (<span>if</span> (&gt; (length head) 7) (substring head 0 7) head))
                 (flags (string-join
                         (delq nil
                               (list (<span>when</span> (plist-get e <span>:bare</span>) <span>"bare"</span>)
                                     (<span>when</span> (plist-get e <span>:detached</span>) <span>"detached"</span>)
                                     (<span>when</span> (plist-get e <span>:locked</span>) <span>"locked"</span>)
                                     (<span>when</span> (plist-get e <span>:prunable</span>)
                                       <span>"prunable"</span>)))
                         <span>","</span>)))
            (insert-button path
                           'action (<span>lambda</span> (btn)
                                      (vc-dir (button-label btn)))
                           'follow-link t)
            (insert (format <span>"  [%s] %s%s\n"</span>
                            (<span>or</span> (plist-get e <span>:branch</span>) <span>"HEAD"</span>)
                            short
                            (<span>if</span> (string-empty-p flags)
                                <span>""</span>
                              (concat <span>" ("</span> flags <span>")"</span>)))))))
      (goto-char (point-min))
      (pop-to-buffer (current-buffer)))))<br>
(<span>with-eval-after-load</span> 'vc-dir
  (define-key vc-dir-mode-map (kbd <span>"Z a"</span>) #'my/vc-git-worktree-add)
  (define-key vc-dir-mode-map (kbd <span>"Z b"</span>) #'my/vc-git-worktree-checkout)
  (define-key vc-dir-mode-map (kbd <span>"Z c"</span>) #'my/vc-git-worktree-branch)
  (define-key vc-dir-mode-map (kbd <span>"Z g"</span>) #'my/vc-git-worktree-switch)
  (define-key vc-dir-mode-map (kbd <span>"Z k"</span>) #'my/vc-git-worktree-remove)
  (define-key vc-dir-mode-map (kbd <span>"Z l"</span>) #'my/vc-git-worktree-list)
  (define-key vc-dir-mode-map (kbd <span>"Z m"</span>) #'my/vc-git-worktree-move)
  (define-key vc-dir-mode-map (kbd <span>"Z p"</span>) #'my/vc-git-worktree-prune))
</pre>
</div><br>
<p>
Some interactive commands: <code>Z a</code> for the general case, <code>Z b</code> for checking out an existing branch, <code>Z c</code> for a new branch from a start point, plus <code>Z m</code> to move a tree, <code>Z g</code> to switch between them with branch annotations in the completion, <code>Z k</code> to remove one (prefix forces, and a <code>vc-dir</code> sitting inside the removed tree falls back to the main root rather than dying), <code>Z p</code> to prune stale metadata, and <code>Z l</code> for a listing buffer where <code>RET</code> jumps to <code>vc-dir</code>. The letters deliberately mirror Magit's own worktree map, so <code>Z b</code>, <code>Z c</code>, <code>Z g</code>, <code>Z m</code> and <code>Z k</code> all do what a Magit user's fingers expect; <code>Z a</code>, <code>Z p</code> and <code>Z l</code> are extras with no Magit equivalent. The same set hangs off <code>C-x v z …</code> globally.
</p>

<p>
So the whole loop, start to finish, never leaves <code>vc</code>: <code>Z c</code> to spin up the worktree, hack away, <code>C-x v v</code> to commit, <code>Z g</code> back to main, merge the branch, and <code>Z k</code> to remove the worktree.
</p>

<p>
If any of this sounds useful, the pattern to steal is a small one: <code>vc-git-command</code> with <code>default-directory</code> bound to the worktree root does almost all of the heavy lifting, and <code>vc-dir</code> on the resulting path gives you the status buffer for free. The rest is just prompts and keybindings.
</p>
</body></html>]]></content>
        <author>
            <name>James Dyer</name>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Magnus: Emacs salmagundi, 2026-09-06]]></title>
        <id>https://magnus.therning.org/2026-09-06-emacs-salmagundi,-2026-09-06.html</id>
        <link href="https://magnus.therning.org/2026-09-06-emacs-salmagundi,-2026-09-06.html"/>
        <updated>2026-09-06T16:33:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><div class="outline-2">
<h2>Compilation commands by project type</h2>
<div class="outline-text-2">
<p>
I while ago I added a minor mode, <code>haskell-ng-project-mode</code>, to my Haskell mode.
The idea I had was that I could use it to add keybindings for running various
tools (via <code>compile</code>), e.g. I bound <code>cabal build</code> and <code>cabal test</code> <code>, p b</code> and
<code>, p t</code> respectively. Then I hooked in the minor mode in my <code>haskell-ng-mode</code>.
Soon I realised it'd be nice to have those shortcuts available in <code>dired</code> too,
so I hooked it in there as well. When I later wanted to re-run the tests after
modifying a JSON file that was part of a golden test I realised that this wasn't
a very nice solution at all.
</p>

<p>
After looking around a bit I found <a href="https://github.com/ReanGD/emacs-multi-compile">emacs-multi-compile</a>. I removed the minor mode
and add this configuration instead
</p>

<div class="org-src-container">
<pre><code><span class="org-rainbow-delimiters-depth-1">(</span>add-to-list 'multi-compile-alist
             '<span class="org-rainbow-delimiters-depth-2">(</span><span class="org-rainbow-delimiters-depth-3">(</span>haskell-ng-is-project<span class="org-rainbow-delimiters-depth-3">)</span> . <span class="org-rainbow-delimiters-depth-3">(</span><span class="org-rainbow-delimiters-depth-4">(</span><span class="org-string">"cabal build"</span> <span class="org-string">"cabal build -j --semaphore"</span> <span class="org-rainbow-delimiters-depth-5">(</span>project-root <span class="org-rainbow-delimiters-depth-6">(</span>project-current<span class="org-rainbow-delimiters-depth-6">)</span><span class="org-rainbow-delimiters-depth-5">)</span><span class="org-rainbow-delimiters-depth-4">)</span>
                                          <span class="org-rainbow-delimiters-depth-4">(</span><span class="org-string">"cabal test"</span> <span class="org-string">"cabal test"</span> <span class="org-rainbow-delimiters-depth-5">(</span>project-root <span class="org-rainbow-delimiters-depth-6">(</span>project-current<span class="org-rainbow-delimiters-depth-6">)</span><span class="org-rainbow-delimiters-depth-5">)</span><span class="org-rainbow-delimiters-depth-4">)</span>
                                          <span class="org-rainbow-delimiters-depth-4">(</span><span class="org-string">"fourmolu"</span> <span class="org-string">"fourmolu -i $(fd .hs$)"</span> <span class="org-rainbow-delimiters-depth-5">(</span>project-root <span class="org-rainbow-delimiters-depth-6">(</span>project-current<span class="org-rainbow-delimiters-depth-6">)</span><span class="org-rainbow-delimiters-depth-5">)</span><span class="org-rainbow-delimiters-depth-4">)</span><span class="org-rainbow-delimiters-depth-3">)</span><span class="org-rainbow-delimiters-depth-2">)</span><span class="org-rainbow-delimiters-depth-1">)</span>
</code></pre>
</div>

<p>
I've had some vague thoughts that it'd be rather easy to write something
slightly more custom, but <code>emacs-multi-compile</code> works very well for me so I'm
very happy with it.
</p>
</div>
</div>
<div class="outline-2">
<h2>Jumping between implementation and test, again</h2>
<div class="outline-text-2">
<p>
I wrote about this <a href="https://magnus.therning.org/2026-02-18-switching-to-project.el.html">a while ago</a>. At the time I came up with a way of bending
<code>ff-find-other-file</code> to my will, but soon after that I ran into a case where
there may be 2 "other files" – one of the Haskell projects at work put unit
tests in files ending in <code>Spec.hs</code> and property tests in filed ending with
<code>Prop.hs</code>. Unfortunately this is a situation that <code>ff-find-other-file</code> can't
handle.
</p>

<p>
I ended up hacking together <a href="https://gitlab.com/magus/mep-project-extras/-/blob/main/consult-kith.el"><code>consult-kith</code></a> to deal with it. The configuration to
handle two kinds of test files looks like this
</p>

<div class="org-src-container">
<pre><code><span class="org-rainbow-delimiters-depth-1">(</span><span class="org-keyword">setq-local</span> consult-kith-alist `<span class="org-rainbow-delimiters-depth-2">(</span><span class="org-rainbow-delimiters-depth-3">(</span>,<span class="org-rainbow-delimiters-depth-4">(</span><span class="org-keyword">rx</span> <span class="org-rainbow-delimiters-depth-5">(</span>seq <span class="org-string">"Spec.hs"</span> eol<span class="org-rainbow-delimiters-depth-5">)</span><span class="org-rainbow-delimiters-depth-4">)</span> <span class="org-rainbow-delimiters-depth-4">(</span><span class="org-string">".hs"</span><span class="org-rainbow-delimiters-depth-4">)</span><span class="org-rainbow-delimiters-depth-3">)</span>
                                 <span class="org-rainbow-delimiters-depth-3">(</span>,<span class="org-rainbow-delimiters-depth-4">(</span><span class="org-keyword">rx</span> <span class="org-rainbow-delimiters-depth-5">(</span>seq <span class="org-string">"Prop.hs"</span> eol<span class="org-rainbow-delimiters-depth-5">)</span><span class="org-rainbow-delimiters-depth-4">)</span> <span class="org-rainbow-delimiters-depth-4">(</span><span class="org-string">".hs"</span><span class="org-rainbow-delimiters-depth-4">)</span><span class="org-rainbow-delimiters-depth-3">)</span>
                                 <span class="org-rainbow-delimiters-depth-3">(</span>,<span class="org-rainbow-delimiters-depth-4">(</span><span class="org-keyword">rx</span> <span class="org-rainbow-delimiters-depth-5">(</span>seq <span class="org-string">".hs"</span> eol<span class="org-rainbow-delimiters-depth-5">)</span><span class="org-rainbow-delimiters-depth-4">)</span> <span class="org-rainbow-delimiters-depth-4">(</span><span class="org-string">"Prop.hs"</span> <span class="org-string">"Spec.hs"</span><span class="org-rainbow-delimiters-depth-4">)</span><span class="org-rainbow-delimiters-depth-3">)</span><span class="org-rainbow-delimiters-depth-2">)</span>
            consult-kith-search-directories '<span class="org-rainbow-delimiters-depth-2">(</span><span class="org-string">"src"</span> <span class="org-string">"test"</span><span class="org-rainbow-delimiters-depth-2">)</span><span class="org-rainbow-delimiters-depth-1">)</span>
</code></pre>
</div>

<p>
The project has since settled on only using <code>Spec.hs</code> for tests, but I'm
sticking to <code>consult-kith</code> for now.
</p>
</div>
</div>
<div class="taglist"><a href="https://magnus.therning.org/tags.html"><span class="tag-label">Tags</span></a><span class="tag-separator">: </span><span class="taglist__tags"><a href="https://magnus.therning.org/tag-emacs.html">emacs</a> </span></div>
</body></html>]]></content>
        <author>
            <name>Magnus</name>
            <uri>https://magnus.therning.org//tag-emacs.html</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Irreal: Prot’s Doric Themes]]></title>
        <id>https://irreal.org/blog/?p=14064</id>
        <link href="https://irreal.org/blog/?p=14064"/>
        <updated>2026-09-06T14:48:40.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
As many of you know, my idea of a theme is to set the background to “oldlace” and the cursor to red. For the rest of it, I just take what Emacs gives me. That sounds pretty haphazard but it works out pretty well. It has enough contrast to be easy to read without searing my eyeballs and has enough syntax highlighting to be useful without being overpowering.
</p>
<p>
Prot (Protesilaos Stavrou) has a much more refined sense for these things. He considers what colors go well to together and things like contrast levels. Just the oposite of my “do with me what you will” approach. Nevertheless, I mostly don’t bother much with formally defined themes although I do occasionally like to take a look at them.
</p>
<p>
The reason for this post is Prot’s latest release of his <a href="https://protesilaos.com/codelog/2026-09-04-doric-themes-1-3-0/">Doric Themes</a>. They’re sort of minimalist, like my non-theme theme but still very nice looking if you don’t want your Emacs screen to look like an arcade. The doric-almond variation is, in particular, very similar to my color scheme. Mine has a little more contrast: the foreground text is a bit darker and the keywords in my code buffers are a blue that’s slightly brighter than doric-almond’s light green. Still, it’s a theme that I’d be comfortable with if I hadn’t taken the easy way.
</p>
<p>
Some people have multiple themes and change them according to the time of day, their mood, the phase of the moon, or whatever. I’d hate that probably for the same reason that I don’t do well with trying to use different editors: I’d always be confused as to what’s what.
</p>
<p>
If you’re looking for a minimalist theme and my method seems <i>too</i> minimal, take a look at Prot’s Doric Themes. There are several variations in both light and dark mode.</p>
</body></html>]]></content>
        <author>
            <name>Irreal</name>
            <uri>https://irreal.org/blog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Joar von Arndt: Emacs does not need LSP]]></title>
        <id>https://joarvarndt.se/emacs-lsp.html</id>
        <link href="https://joarvarndt.se/emacs-lsp.html"/>
        <updated>2026-09-05T16:00:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><h2></h2>
<nav>
<h2>Table of Contents</h2>
<div>
<ul>
<li><a href="https://joarvarndt.se//tag-emacs.html#orgc794c94">In-buffer completion</a></li>
<li><a href="https://joarvarndt.se//tag-emacs.html#org70b1f52">Jump to definition and references</a></li>
<li><a href="https://joarvarndt.se//tag-emacs.html#org7638b5b">Syntax checking</a></li>
<li><a href="https://joarvarndt.se//tag-emacs.html#orgbe9f265">Conclusion</a></li>
</ul>
</div>
</nav>
<hr>

<p>
Language-server-protocol (<span class="small-caps">lsp</span>) is standard to communicate information about a
current programming project to an editor integration, powered by a “language
server” for the specific programming language. Emacs has two major
implementations of this editor functionality: <code>eglot</code> (built-in) and <code>lsp-mode</code>.
Getting this set up is usually one of the first things that new users will want
to do, but in my experience one can get by for a surprisingly long time using
Emacs without needing to reach for <code>eglot</code> or <code>lsp-mode</code>. That is because many of
the functions that <span class="small-caps">lsp</span>-servers provide can be provided by Emacs through other
means.
</p>

<p>
As someone who quite often moves between different programming languages, I find
the need to set up each <span class="small-caps">lsp</span>-server to a bit clunky. Despite being presented as a
simple one-stop solution, you often need to research exactly what server to use,
how to set it up, and to tailor it to your machine in some ways. For me, this
only makes sense in a few languages that I use often, and even then it might not
be the first thing I take care of.
</p>
<div class="outline-2">
<h2>In-buffer completion</h2>
<div class="outline-text-2">
<p>
The most obvious functionality is auto-complete (<span class="small-caps">aka</span> in-buffer completion). In
Emacs this is provided on multiple levels, which is sometimes confusing for new
users. There are many ways to <i>show</i> in-buffer completions, but they are all
powered by the same back-end functionality: <code>completion-at-point-functions</code>
(<span class="small-caps">capf</span>). This is a list of functions that run in order, only returning a value if
there is something to complete.
</p>

<p>
Some major modes (most notable perhaps <code>lisp-interaction-mode</code>) come with their
own buffer-local values of <span class="small-caps">capf</span><sup><a href="https://joarvarndt.se//tag-emacs.html#fn.1">1</a></sup> that provide buffer-specific completions.
<code>completion-at-point</code> will then move on to the global value if the buffer-local
value of <span class="small-caps">capf</span> includes the value <code>t</code>. It is therefore this global value that we
are often interested in modifying.
</p>

<p>
For this we can use the <code>cape</code>-package — standing for “Completion At Point
Functions”. Specifically, we can make liberal use of the <code>cape-dabbrev</code>-function.
This <span class="small-caps">capf</span> makes use of Emacs’ built-in dabbrev-functionality. Dabbrev will scan
all the words in the buffers of your choice and give you completions for words
that you have already used. This means that, while working on your project, you
have quick and easy access to all the keywords and function names in your
currently opened buffers.
</p>

<p>
This also works as a form of autocomplete for writing prose, and so we can
combine these two using <code>cape-wrap-super</code>:
</p>

<div class="org-src-container">
<pre>(<span class="org-keyword">defun</span> <span class="org-function-name">cape-language</span> (<span class="org-type">&amp;optional</span> interactive)
    (<span class="org-keyword">interactive</span> (list t))
    (<span class="org-keyword">if</span> interactive
        (cape-interactive #'cape-language)
      (cape-wrap-super #'cape-dabbrev #'cape-dict)))
</pre>
</div>

<p>
This will combine the output <code>cape-dabbrev</code> and <code>cape-dict</code><sup><a href="https://joarvarndt.se//tag-emacs.html#fn.2">2</a></sup> into one <span class="small-caps">capf</span> that
can run at the same time (and therefore not block each other). For a simpler
contribution we also use the <code>cape-keyword</code> <span class="small-caps">capf</span> that comes with a number of
pre-configured programming language<sup><a href="https://joarvarndt.se//tag-emacs.html#fn.3">3</a></sup> keywords. This is a bit of a pre-<span class="small-caps">lsp</span>
solution, where each editor provided support for each programming language. To
set up <code>cape</code>, simple add something like this to your init file:
</p>

<div class="org-src-container">
<pre>(add-hook 'completion-at-point-functions #'cape-history)
(add-hook 'completion-at-point-functions #'cape-keyword)
(add-hook 'completion-at-point-functions #'cape-file)
(add-hook 'completion-at-point-functions #'cape-language)
(add-hook 'completion-at-point-functions #'cape-abbrev)
</pre>
</div>

<p>
This is the order in which <code>cape</code> will try your <span class="small-caps">capf</span>s, with the first matching
blocking the rest. As mentioned earlier, locally bound <span class="small-caps">capf</span>s will supersede
these global values, and so <code>python-completion-at-point</code> will run before any of
these in <code>python-mode</code> buffers.
</p>

<p>
Another improvement we can do is to change the value of
<code>cape-dabbrev-buffer-function</code>. The value of this variable is the function that
returns the buffers to scan for dabbrev results. By default this is
<code>cape-same-mode-buffers</code>, meaning that only buffers in the same mode (org-mode,
python-mode, <i>et cetera</i>) will be used for results. This is a good default, but I
would rather have always have <i>some</i> relevant completion candidate than nothing.
So personally I set this value to <code>cape-text-buffers</code>, which will return all
buffers in text-mode or prog-mode buffers. This means extra niceties like
referring to practically anything you are working on when writing a commit
message in git.
</p>
</div>
</div>
<div class="outline-2">
<h2>Jump to definition and references</h2>
<div class="outline-text-2">
<p>
A second major feature of <span class="small-caps">lsp</span>-servers is “jump to definition” that allows you to
go to the code that creates a function (its <i>definition</i>) to inspect its inner
workings. There are a few different ways to do this.
</p>

<p>
The first is the package named <a href="https://github.com/jacktasia/dumb-jump"><code>dumb-jump</code></a> that uses <code>ag</code>, <code>rg</code>, or simply <code>grep</code> to
find definition-matching strings. This works similarly to <code>cape-keyword</code> in that
it uses a pre-configured set of language-syntax elements to match
function-defining cases.<sup><a href="https://joarvarndt.se//tag-emacs.html#fn.4">4</a></sup> I however do not personally use this.
</p>

<p>
Instead I use another of <a href="https://github.com/minad">Minad</a>’s wonderful additions to the Emacs’ ecosystem;
<a href="https://github.com/minad/consult"><code>consult</code></a>. I must confess that I slept on consult for far too long. I had it in
my config for a long time, but the number of (largely stand-alone) functions
made it a bit overwhelming to learn.<sup><a href="https://joarvarndt.se//tag-emacs.html#fn.5">5</a></sup> What Consult offers is a suite of
improved versions of preëxisting Emacs utilities (and a few new ones building
upon said features), largely based on the <code>completing-read</code>.
</p>

<p>
Emacs has a built-in feature called <code>imenu</code> that scans the buffer’s structure for
top-level features (Org-mode and markdown headers, or in this case function
definitions) and allows you to quickly move to them. Consult of course expands
on this by instantly showing you the place you will move to, but it also offers
an extension of this feature: <code>consult-imenu-multi</code> (as opposed to the regular
<code>consult-imenu</code>). This will not only scan the current buffer, but also all other
same-mode buffers. Better yet, it integrates with Emacs’ <code>project.el</code>, and so only
checks buffers that belong to the same project (<i>exempli gratia</i> git repository).
As I usually have the relevant buffers open in a project I am working on, this
works very well as an alternative to <code>dumb-jump</code>, and the preview and subsequent
confirmation mean that I am not worried about jumping to the wrong place —
although of course at the cost of speed.
</p>

<p>
Then we of course have the venerable <code>xref</code> built into Emacs. This works a bit
differently depending on the programming language backend, and plugs into an
<span class="small-caps">lsp</span>-server automatically if it is available. Use <code>xref-find-references</code><sup><a href="https://joarvarndt.se//tag-emacs.html#fn.6">6</a></sup>
Otherwise we can use <code>etags-regen</code> to automatically create and refresh the <code>TAGS</code>
file that keeps track of the project structure:
</p>

<div class="org-src-container">
<pre>(<span class="org-keyword">use-package</span> etags-regen
  <span class="org-builtin">:config</span>
  (<span class="org-keyword">setq</span> etags-regen-ignores
        '(<span class="org-string">"*.pyc"</span> <span class="org-string">".git"</span> <span class="org-string">".venv"</span> <span class="org-string">"venv"</span> <span class="org-string">"node_modules"</span>))
  (etags-regen-mode 1))
</pre>
</div>

<p>
Consult can also be used to improve upon this as well by replacing the <span class="small-caps">ui</span>
interaction component:
</p>

<div class="org-src-container">
<pre>(<span class="org-keyword">setq</span> xref-show-xrefs-function #'consult-xref
      xref-show-definitions-function #'consult-xref)
</pre>
</div>

<p>
The reason why I usually prefer using <code>imenu</code> is because it requires no trip to
inspect the state of any files on the system — everything is already loaded and
available, and perhaps 90% of the time it includes the things that I need (since
they are things I am working on).
</p>
</div>
</div>
<div class="outline-2">
<h2>Syntax checking</h2>
<div class="outline-text-2">
<p>
Here we can use the built-in <code>flymake</code> or the standalone <code>flycheck</code>. Both of these
make use of external syntax-checking tools for each language, and so do require
some more setup on the host machine than the above solutions. In fact, Flycheck
can even act as its own <a href="https://emacsredux.com/blog/2026/07/29/flycheck-38/"><span class="small-caps">lsp</span>-server</a> just for syntax checking. But my experience
has still been that this is easier to deal with than <span class="small-caps">lsp</span>-servers that need to be
configured, started, reconnected, that crash, or have some other issue that need
to be dealt with; A simple <span class="small-caps">unix</span>-style shell command that prints text (and that
is then processed by Flymake or Flycheck) rarely needs much setup or maintenance
in my experience. This is a matter that I really just want out of my mind.
</p>
</div>
</div>
<div class="outline-2">
<h2>Conclusion</h2>
<div class="outline-text-2">
<p>
There are only so many things that can be done merely through these static
solutions. For example, neither of our in-buffer completion or
jump-to-definition solution will complete external libraries; only buffers that
have already been opened. There is no hover information.
</p>

<p>
I am not ideologically opposed to the use of language servers, and I will
sometimes intentionally start one if I expect to be working on a larger code
base for a longer amount of time. But tools like the ones above are nevertheless
incredibly useful because they work in all buffers and contexts.
</p>

<p>
One of the strengths of Emacs is how it acts as a sort of force-multiplier for
any task interacting with text, and how it builds upon improvements in one area
throughout the entire editor. Having some of the most critical <span class="small-caps">lsp</span>-like
abilities available anywhere you want without any extra effort is incredibly
powerful. This is especially noteworthy for things like <code>cape-dabbrev</code> since it
has made me so spoiled for autocompletion for everything I write.
</p>

<p>
I am sure that there are lots of different solutions and tools that I have
missed that would work to replace even more <span class="small-caps">lsp</span>-based ones, and probably
solutions that work better and/or are more elegant than the ones I use — the
answer may even be to just use <span class="small-caps">lsp</span>. But honestly I only think about the matter
of language server when I am talking with others. In day to day life I rarely
feel the need for them at all. ❦
</p>
</div>
</div>
<div>
<h2>Footnotes: </h2>
<div>

<div class="footdef"><sup><a href="https://joarvarndt.se//tag-emacs.html#fnr.1">1</a></sup> <div class="footpara"><p>
For <code>lisp-interaction-mode</code> the value is <code>(elisp-completion-at-point
t)</code>.
</p></div></div>

<div class="footdef"><sup><a href="https://joarvarndt.se//tag-emacs.html#fnr.2">2</a></sup> <div class="footpara"><p>
The results of <code>cape-dict</code> are decided by the value of the
<code>cape-dict-file</code> variable.
</p></div></div>

<div class="footdef"><sup><a href="https://joarvarndt.se//tag-emacs.html#fnr.3">3</a></sup> <div class="footpara"><p>
Here is the list of programming languages supported:
</p>

<p>
C++, C, Caml, Crystal, C#, D, Elixir, Erlang, <span class="small-caps">f90</span>, Go, Java,
Javascript, Kotlin, Lua, Nim. Objective C, Perl, <span class="small-caps">php</span>, Purescript,
Python, Ruby, Rust, Scala, Scheme, Swift, Julia, Thrift, sh.
</p></div></div>

<div class="footdef"><sup><a href="https://joarvarndt.se//tag-emacs.html#fnr.4">4</a></sup> <div class="footpara"><p>
This supposedly misidentifies things sometimes; as expected, it is “dumb”
after all.
</p></div></div>

<div class="footdef"><sup><a href="https://joarvarndt.se//tag-emacs.html#fnr.5">5</a></sup> <div class="footpara"><p>
If you are using the built-in <code>switch-to-buffer</code> command (by default bound to
<code>C-x b</code>) I highly recommend switching it to <code>consult-buffer</code> instead. Instant
“previews” is super useful, and I often just spam <code>C-n</code> (N being right next to B)
to cycle through my open buffers instead of starting to write the exact name
that I want.
</p></div></div>

<div class="footdef"><sup><a href="https://joarvarndt.se//tag-emacs.html#fnr.6">6</a></sup> <div class="footpara"><p>
By default bound to <code>M-?</code>. To run <code>xref-go-back</code> press <code>M-,</code>.
</p></div></div>


</div>
</div>
</body></html>]]></content>
        <author>
            <name>Joar von Arndt</name>
            <uri>https://joarvarndt.se//tag-emacs.html</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Irreal: Optimizing Startup With Use-package]]></title>
        <id>https://irreal.org/blog/?p=14062</id>
        <link href="https://irreal.org/blog/?p=14062"/>
        <updated>2026-09-05T14:44:09.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
James Cherti has another excellent post, this time on <a href="https://www.jamescherti.com/emacs-startup-defer-use-package-performance/">optimizing Emacs startup with use-package</a>. The idea is that many configurations load a substantial number of packages and that this can cause a significant increase in startup time.
</p>
<p>
Most of you know by now that we here at the Irreal bunker refuse to worry about that because, after all, we hardly ever restart Emacs. Still, plenty of people <i>do</i> worry about it and there’s nothing wrong with learning a few easy to implement principles that will speedup Emacs startup without having to obsess about tiny details.
</p>
<p>
The TL;DR is to get <code>use-package</code> to defer loading of packages until they are actually needed. Most Emacers who aren’t complete n00bs already know this. The question is, how exactly, to specify the <code>use-package</code> commands to achieve that.
</p>
<p>
The obvious answers aren’t the right ones. For example, the <code>:defer</code> keyword seems like the natural answer but Cherti explains why this isn’t usually necessary and that it doesn’t do what you probably think it does. It’s much better to use one of the commands that implicitly defer loading but also arrange for autoloading the required functions when they’re needed.
</p>
<p>
Cherti also explains how to get <code>use-package</code> to add logging information to the <code>*Messages*</code> buffer to help with understanding the load process. There’s a lot of information in Cherti’s post and it serves as a handy reference for how to use <code>use-package</code> to control the loading of packages at startup time. If you’re at all worried about startup time or just want to understand how to use <code>use-package</code> to control package loading, take a look at his post.</p>
</body></html>]]></content>
        <author>
            <name>Irreal</name>
            <uri>https://irreal.org/blog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Sacha Chua: Emacs Carnival September 2026: Games]]></title>
        <id>https://sachachua.com/blog/2026/09/emacs-carnival-september-2026-games/</id>
        <link href="https://sachachua.com/blog/2026/09/emacs-carnival-september-2026-games/"/>
        <updated>2026-09-04T01:04:46.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><div class="update">
<p>
The theme for the Emacs Carnival this September is "<a href="https://www.emacswiki.org/emacs/CarnivalSeptember2026">Games</a>". Thanks to <a href="https://ray-on-emacs.blogspot.com/2026/09/games-emacs-carnival-post-for-september.html">Raymond Zeitler</a> for hosting!
</p>

</div>

<p>
One of the things I love about the Emacs community is people's sense of humour and whimsy. There's a grand tradition of people hiding <a href="https://en.wikipedia.org/wiki/Easter_egg_(media)">Easter eggs</a> in software, a bit of hidden fun that might make someone smile.
But isn't this just bloat and frivolity? One might argue that the code that lets us play <code>M-x tetris</code>, <code>dunnet</code>, or <code>doctor</code> in Emacs is a waste of time and space compared to More Serious Things, but on the other hand, the <a href="https://github.com/emacs-mirror/emacs/blob/master/lisp/play/tetris.el">Tetris</a> source code could be a good jumping-off point if you find yourself wanting to make an interactive real-time simulation that displays graphical results. Want something more modern? Maybe <a href="https://github.com/minad/doom-on-emacs">DOOM on Emacs</a>!
Besides, the code for games isn't actually loaded until you choose to play it. It's okay for something to be fun by itself instead of needing to be productive. There's something wonderful about a tool that people choose not just for work but also for enjoyment.
</p>

<p>
Our adventure can start with <a href="https://www.masteringemacs.org/article/fun-games-in-emacs">Fun and Games in Emacs - Mastering Emacs</a> or <a href="https://www.youtube.com/watch?v=DM41Nf4-tQM">Emacs Is A Gaming Platform for Windows, Mac and Linux - YouTube</a>, which cover a lot of classic built-ins. Here's a barrage of ideas; skip or explore anything you want: <a href="https://github.com/rbanffy/selectric-mode">Pretend you're on a typewriter</a> or turn on <a href="https://github.com/elizagamedev/power-mode.el">power mode</a> or <a href="https://github.com/CestDiego/c-c-combo.el">make c-c-combos</a>. Go on to <a href="https://www.emacs.dyerdwelling.family/emacs/20231027211128-emacs--famous-film-using-artist-mode/">draw some ASCII art with artist-mode</a> or <a href="https://dataswamp.org/~incal/bad-www/index.html">with Emacs Lisp</a>, and add some <a href="https://github.com/tbanel/uniline">box shadows</a> because why not. Maybe even use it to <a href="https://mbork.pl/2025-09-08_Emacs_Artist_clock">tell the time</a>. <a href="https://gitlab.com/xgqt/emacs-ansilove">Turn ASCII art into PNGs</a>. Have fun with <a href="https://www.reddit.com/r/emacs/comments/1cr7515/for_some_reason_the_emacs_calendar_can_display/">obscure calendars</a>. <a href="https://zwit.link/posts/kaomel-emacs-package/">Use kaomojis</a> or <a href="https://github.com/nehrbash/zalgo-mode/tree/main">write weird text</a>. Explore the <a href="https://indieweb.social/@xenodium/115493728712490397">screensavers in zone</a> and <a>let them sprinkle characters through your buffer</a>, <a>take over your other frames</a>, or <a href="https://xenodium.com/emacs-zones-to-lift-you-up">teach you something new</a>. Watch <a href="https://github.com/alphapapa/snow.el">snow</a> or a <a href="https://github.com/johanvts/emacs-fireplace">fireplace</a>. Rotate <a href="https://github.com/bchatterjee99/emacs-ascii-cube">cubes in ASCII</a> or with <a href="https://emacsconf.org/2025/talks/graphics/">hardware-accelerated graphics</a>. <a href="https://www.reddit.com/r/emacs/comments/1vxkrx3/updated_my_obglsl_module_to_use_emacs_32_canvas/">Why limit yourself to cubes</a>? Still, if you do want to stick to cubes, you can <a href="https://codeberg.org/akib/emacs-cube">solve them</a>. Go ahead, <a href="https://blog.josephwilk.net/art/emacs-animation.html">animate something.</a> <a href="https://www.ttrpg-hangout.com/solo_rpg_mode_for_emacs.html">Play roleplaying games</a> (also check out <a href="https://emacsconf.org/2023/talks/solo/">Howard Abrams's 2023 talk</a>), <a href="https://www.reddit.com/r/emacs/comments/1o60kvj/nethackel_version_0150_released/">rogue-likes,</a> <a href="https://codeberg.org/nosrednayduj/moo-el">multi-user dungeons</a>, <a href="https://github.com/dankeyy/el-chipo">classics</a>, <a href="https://en.andros.dev/blog/d8b3a759/playing-chess-online-with-emacs/">chess</a>, <a href="https://www.salkosuo.net/2015/10/22/elite-for-emacs.html">trading</a>, <a href="https://codeberg.org/tomenzgg/Emacs-Klondike">Klondike</a> and lots of other <a href="https://www.reddit.com/r/emacs/comments/1w07wyt/rfc_cardgames_melpa_thirtyfive_single_and/">card games</a>, <a href="https://codeberg.org/monadicsheep/stk-code">a racing game</a>, <a href="https://hg.sr.ht/~zck/minesweeper">Minesweeper</a>, <a href="https://hg.sr.ht/~zck/game-2048">2048</a>, <a href="https://wasamasa.itch.io/xcb-boomshine">Boomshine</a>, <a href="https://github.com/vreeze/eboy">a Gameboy emulator</a> (this other one handles <a href="https://github.com/gongo/emacs-nes">NES</a>). Simulate <a href="https://github.com/vkazanov/elcity/">cities</a> or <a href="https://hg.sr.ht/~zck/sand.el">physics</a> or <a href="https://sr.ht/~vidak/uwu.el/">pets</a> (<a href="https://github.com/tiatatida/tamaemacs">here's another one</a>) or <a href="https://github.com/Lindydancer/gameoflife">life</a>. Have fun with so many cats: <a href="https://github.com/kn66/pomo-cat.el">timers</a>, <a href="https://github.com/TeMPOraL/nyan-mode">scrollcats</a>, <a href="https://github.com/emacsmirror/zone-nyan">screensavers</a>. Not a cat person? There's a <a href="https://github.com/dp12/parrot">parrot</a> version and a <a href="https://github.com/mattmonja/flappy-fish">fish</a> version. <a href="https://github.com/zenitsu7772000/manga-reader">Read comics</a> or <a href="https://lars.ingebrigtsen.no/2016/06/27/an-emacs-meme-generator/">make memes</a>. <a href="https://github.com/progfolio/wordel">Guess words</a> or <a href="https://github.com/LensPlaysGames/word-search-generator--emacs-lisp">find them</a>. <a href="https://www.youtube.com/watch?v=L5-eei1Ouqw">Learn other alphabets</a>. <a href="https://github.com/oantolin/luggage">Make art</a>. You can even turn your Emacs into a <a href="https://github.com/mpardalos/gc-geiger">Geiger counter for garbage collection</a>.
</p>

<p>
Then take a step back and marvel at this community of people who try things out, tinker, follow that crazy idea that won't let go of them, and share what they came up with. If you write Emacs Lisp (or want to learn), rummage around in the source code for ideas on how to do things. See if you have any ideas that tickle your fancy. You might be able to get Emacs to do things text editors don't usually do! =)
</p>

<div class="update">
<p>
Check out the Emacs Carnival page for <a href="https://www.emacswiki.org/emacs/CarnivalSeptember2026">Games</a> to see other posts!
</p>

</div>
<div><a href="https://sachachua.com/blog/2026/09/emacs-carnival-september-2026-games/index.org">View Org source for this post</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F09%2Femacs-carnival-september-2026-games%2F&amp;body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p></body></html>]]></content>
        <author>
            <name>Sacha Chua</name>
            <uri>https://sachachua.com/blog/category/emacs/feed/index.xml</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Protesilaos: Emacs: doric-themes version 1.3.0]]></title>
        <id>https://protesilaos.com/codelog/2026-09-04-doric-themes-1-3-0/</id>
        <link href="https://protesilaos.com/codelog/2026-09-04-doric-themes-1-3-0/"/>
        <updated>2026-09-04T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>These are my minimalist themes. They use few colours and will appear
mostly monochromatic in many contexts. Styles involve the careful use
of typography, such as italics and bold italics.</p>

<p>If you want maximalist themes in terms of colour, check my <code class="language-plaintext highlighter-rouge">ef-themes</code>
package. For something in-between, which I would consider the best
“default theme” for a text editor, opt for my <code class="language-plaintext highlighter-rouge">modus-themes</code>.</p>

<ul>
  <li>Package name (GNU ELPA): <code class="language-plaintext highlighter-rouge">doric-themes</code></li>
  <li>Sample pictures: <a href="https://protesilaos.com/emacs/doric-themes-pictures">https://protesilaos.com/emacs/doric-themes-pictures</a></li>
  <li>Git repository: <a href="https://github.com/protesilaos/doric-themes">https://github.com/protesilaos/doric-themes</a></li>
  <li>Backronym: Doric Only Really Intensifies Conservatively … themes.</li>
</ul>

<p>Below are the release notes.</p>

<hr>

<h2>Version 1.3.0 on 2026-09-04</h2>

<h3>New theme screenshots</h3>

<p>All sample images are updated to the current version and cover all the
Doric themes: <a href="https://protesilaos.com/emacs/doric-themes-pictures">https://protesilaos.com/emacs/doric-themes-pictures</a>.</p>

<h3>New <code class="language-plaintext highlighter-rouge">doric-lilac</code> and <code class="language-plaintext highlighter-rouge">doric-borage</code> themes</h3>

<p>I drew inspiration for both themes from plants that exist around my
house. They both use the same colours in different shades and
sequence. <code class="language-plaintext highlighter-rouge">doric-lilac</code> has a light background while <code class="language-plaintext highlighter-rouge">doric-borage</code>
has a dark background.</p>

<h3>More commands to load a theme</h3>

<p>The commands <code class="language-plaintext highlighter-rouge">doric-themes-rotate-light</code> and <code class="language-plaintext highlighter-rouge">doric-themes-rotate-dark</code>
are convenience wrappers of <code class="language-plaintext highlighter-rouge">doric-themes-rotate</code>. As their name
suggests, they are restricted to one kind of theme in the collection.</p>

<p>Similarly, <code class="language-plaintext highlighter-rouge">doric-themes-load-random-light</code> and <code class="language-plaintext highlighter-rouge">doric-themes-load-random-dark</code>
are variants of <code class="language-plaintext highlighter-rouge">doric-themes-load-random</code>.</p>

<p>Thanks to Lucas Jiménez for making this suggestion in issue 28:
<a href="https://github.com/protesilaos/doric-themes/issues/28">https://github.com/protesilaos/doric-themes/issues/28</a>.</p>

<h3>Broader face coverage</h3>

<p>The following packages have some new faces or I tweaked them to look
better with the rest of the theme.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">completion-preview</code></li>
  <li><code class="language-plaintext highlighter-rouge">elfeed</code></li>
  <li><code class="language-plaintext highlighter-rouge">erc</code></li>
  <li><code class="language-plaintext highlighter-rouge">flymake</code></li>
  <li><code class="language-plaintext highlighter-rouge">gnus</code></li>
  <li><code class="language-plaintext highlighter-rouge">mu4e</code></li>
  <li><code class="language-plaintext highlighter-rouge">query-replace</code></li>
</ul>

<h3>Minor refinements to palette values</h3>

<p>I made several small tweaks to several themes. The changes are
probably too fine to notice or they should now look correct enough to
be unnoticeable.</p>
        </body></html>]]></content>
        <author>
            <name>Protesilaos</name>
            <uri>https://protesilaos.com/codelog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[James Cherti: Optimizing Emacs Startup - Guide to Deferred Package Loading with use-package]]></title>
        <id>https://www.jamescherti.com/emacs-startup-defer-use-package-performance/</id>
        <link href="https://www.jamescherti.com/emacs-startup-defer-use-package-performance/"/>
        <updated>2026-09-03T16:36:20.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>As an Emacs user, your configuration can easily grow from a few lightweight adjustments to a massive, hundred-package IDE. Without careful management, Emacs startup time can degrade from sub-second execution to several seconds, or minutes, in the worst cases. Eager package loading is one common source of startup overhead. This guide explains how Emacs loads libraries, how <code>use-package</code> configures package loading, and how deferred loading can reduce startup time.</p>



<p>Before we dive in, please consider sharing this article on your website, blog, Mastodon, Reddit, X, Linkedin, or other social media platforms.</p>



<h2>Why does Emacs startup get slow?</h2>



<p>A naive package declaration looks like this:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; The kirigami Emacs package provides a unified method to fold and unfold</span>
<span class="hljs-comment">;; text in Emacs across a diverse set of Emacs modes.</span>
(<span class="hljs-name">use-package</span> kirigami)</code></span></pre>


<p>By default, this causes <code>use-package</code> to load the package during initialization, unless the declaration contains a deferring keyword or another option that changes the loading behavior.</p>



<p>Conceptually, the eager loading is equivalent to:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">require</span> 'kirigami)</code></span></pre>


<p>The <code>require</code> function ensures that a feature is loaded.</p>



<p>When many packages are loaded this way, the time spent loading and evaluating their code contributes to increasing startup time.</p>



<h2>What is Autoloading?</h2>



<p>Autoloading registers a function without immediately loading the library that defines it. The library is loaded only when the function is called. For example:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Simplified representation of an autoload registration</span>
(<span class="hljs-name">autoload</span> 'kirigami-global-mode <span class="hljs-string">"kirigami"</span> <span class="hljs-string">"Global mode for Kirigami."</span> <span class="hljs-literal">t</span>)</code></span></pre>


<p>If <code>M-x kirigami-global-mode</code> invokes the autoload, Emacs loads the <code>kirigami.el</code> library ("kirigami", the second argument), and then calls the actual <code>kirigami-global-mode</code> function. This allows the library's startup cost to be deferred until the function is actually needed.</p>



<h2>Explicit vs. implicit deferral in use-package</h2>



<p>The <code>use-package</code> macro makes package configuration and loading declarative. By default, a declaration is eager, meaning the package is loaded immediately while Emacs processes the configuration during startup, rather than waiting until the package is needed. A deferring keyword or another loading mechanism can postpone loading until later.</p>



<h3>The explicit :defer keyword</h3>



<h4>Deferral using :defer t (Not necessary)</h4>



<p>Note: Explicitly using <code>:defer t</code> is often unnecessary. Keywords such as <code>:commands</code>, <code>:bind</code>, <code>:hook</code>, <code>:commands</code>, and <code>:mode</code> establish loading triggers automatically. These keywords are discussed below.</p>



<p>If a package does not have another loading trigger, <code>:defer t</code> prevents <code>use-package</code> from loading it immediately:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">use-package</span> kirigami
  <span class="hljs-symbol">:defer</span> <span class="hljs-literal">t</span>)</code></span></pre>


<p>This prevents the package from being required during startup. Without an autoload or another loading trigger, the package will remain unloaded until something else loads it.</p>



<h4>Idle deferral: Numeric argument (Not ideal)</h4>



<p>A numeric value to <code>:defer</code> schedules the package to be loaded after Emacs has been idle for the specified number of seconds:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Load kirigami after Emacs has been idle for 20 seconds</span>
(<span class="hljs-name">use-package</span> kirigami
  <span class="hljs-symbol">:defer</span> <span class="hljs-number">20</span>)</code></span></pre>


<p>Note: This is not ideal because loading a heavy package after a few seconds will block the event loop and can cause a freeze in the user interface. If multiple packages are deferred via idle timers, they can trigger sequentially during a single idle period, increasing the delay. To prevent unprompted blocking operations, it is best practice to configure explicit autoloads, such as <code>:hook</code>, <code>:bind</code>, <code>:commands</code>, or <code>:mode</code>. (These keywords are discussed below.)</p>



<h3>Implicit deferral: Autoload triggers (Recommended solution)</h3>



<p>Instead of manually adding <code>:defer t</code> to every <code>use-package</code> declaration, you can use trigger keywords to configure deferred loading automatically. These keywords generate autoloads, ensuring the package evaluates only when you interact with its components:</p>



<ul>
<li><code>:commands</code>: Generates autoloads for specific interactive commands. Emacs loads the package when you execute the command (for example, via <code>M-x</code>).</li>



<li><code>:bind</code>: Maps keys to commands and automatically creates autoloads for them.</li>



<li><code>:hook</code>: Adds a package function to a hook and arranges for deferred loading.</li>



<li><code>:mode</code>: Adds a file-pattern entry to <code>auto-mode-alist</code> and defers loading until the mode is needed.</li>
</ul>



<h4>Example: Autoloading on major mode file extensions</h4>



<p>Instead of loading Markdown mode immediately, use <code>:mode</code> to defer it until you open a <code>.md</code> file:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">use-package</span> markdown-mode
  <span class="hljs-symbol">:mode</span> (<span class="hljs-string">"\\.md\\'"</span> . markdown-mode))</code></span></pre>


<p>Note: Specifying <code>:defer t</code> is unnecessary here, as <code>:mode</code> automatically defers package loading.</p>



<h3>Example: Autoloading on keybindings</h3>



<p>The <code>:bind</code> keyword maps keys to commands and automatically creates autoloads for them. The package remains deferred until you press one of the defined key combinations:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">use-package</span> embark
  <span class="hljs-symbol">:bind</span>
  ((<span class="hljs-string">"C-."</span> . embark-act)       
   (<span class="hljs-string">"C-;"</span> . embark-dwim)       
   (<span class="hljs-string">"C-h B"</span> . embark-bindings)))</code></span></pre>


<h3>Example: Autoloading specific commands</h3>



<p>If you do not want to define global keybindings but still need to defer loading until a command is called interactively (for example, via <code>M-x</code>), use the <code>:commands</code> keyword. This generates the necessary autoloads:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">use-package</span> embark
  <span class="hljs-symbol">:commands</span> (<span class="hljs-name">embark-act</span>
             embark-dwim
             embark-bindings))</code></span></pre>


<h4>Example: Autoloading on hook execution</h4>



<p>Instead of eagerly loading <code>outline-indent-minor-mode</code> (indentation-based code folding) at startup, you can configure Emacs to load and activate it on demand. This ensures the package evaluates only when a <code>python-mode</code>, <code>python-ts-mode</code>, or <code>yaml-ts-mode</code> buffer is opened:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; The outline-indent Emacs package provides a minor mode for</span>
<span class="hljs-comment">;; indentation-based code folding.</span>
(<span class="hljs-name">use-package</span> outline-indent
  <span class="hljs-symbol">:commands</span> (<span class="hljs-name">outline-indent-minor-mode</span>
             outline-indent-backward-same-level
             outline-indent-forward-same-level)
  <span class="hljs-symbol">:hook</span> ((<span class="hljs-name">python-mode</span> . outline-indent-minor-mode)
         (<span class="hljs-name">python-ts-mode</span> . outline-indent-minor-mode)
         (<span class="hljs-name">yaml-ts-mode</span> . outline-indent-minor-mode))
  <span class="hljs-symbol">:custom</span>
  (<span class="hljs-name">outline-indent-ellipsis</span> <span class="hljs-string">" ▼"</span>))</code></span></pre>


<p>This also loads outline-indent when you invoke the command manually via <code>M-x outline-indent-minor-mode</code>, <code>outline-indent-backward-same-level</code>, or <code>outline-indent-forward-same-level</code>.</p>



<p>Note: There is no need to specify <code>:defer t</code> separately. The <code>:hook</code> keyword adds <code>outline-indent-minor-mode</code> to <code>python-mode-hook</code>, <code>python-ts-mode-hook</code>, and <code>yaml-ts-mode-hook</code>.</p>



<h2>The unconditional use-package :init block</h2>



<p>Code inside the <code>use-package</code> <code>:init</code> section is evaluated before the package is loaded. An autoloaded function called from <code>:init</code> can itself cause the package to load immediately, defeating the intended deferral. Therefore, <code>:init</code> is generally appropriate for settings that must be established before the package loads, while package-dependent function calls belong in <code>:config</code> or another deferred trigger (e.g., <code>with-eval-after-load</code>).</p>



<p>(Similarly, code inside the <code>:preface</code> block evaluates before package loading, but it also executes during byte-compilation. This distinction makes <code>:preface</code> the correct location for code required to satisfy the byte-compiler, such as <code>defvar</code> statements, helper macro definitions, or <code>declare-function</code> calls that prevent compiler warnings for deferred libraries.)</p>



<h3>Forcing immediate load with :demand</h3>



<p>With global deferral enabled, you will occasionally encounter packages that <em>must</em> be loaded immediately (e.g., themes, keybinding managers, or daemon-side servers). You can override global deferral on an individual basis using <code>:demand t</code>:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">use-package</span> tomorrow-night-deepblue-theme
  <span class="hljs-symbol">:demand</span> <span class="hljs-literal">t</span> <span class="hljs-comment">; Force immediate loading</span>
  <span class="hljs-symbol">:config</span>
  <span class="hljs-comment">;; Load the tomorrow-night-deepblue theme</span>
  (<span class="hljs-name">load-theme</span> 'tomorrow-night-deepblue <span class="hljs-literal">t</span>))</code></span></pre>


<p><em>Note: If a declaration specifies both <code>:demand t</code> and <code>:defer t</code> (or triggers), <code>:demand t</code> takes precedence and forces eager loading.</em></p>



<h2>The use-package :after keyword</h2>



<p>The <code>use-package</code> <code>:after</code> keyword allows you to defer the initialization of a package until one or more specified target packages have been fully loaded. This is practical for:</p>



<ul>
<li>Add-on packages that extend a base minor mode.</li>



<li>Integration packages that bridge two distinct tools together.</li>
</ul>



<p>Consider <em>embark-consult</em>, a package that integrates <em>embark</em> with <em>consult</em>. You only need the <em>embark-consult</em> package after <em>embark</em> and <em>consult</em> are loaded:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">use-package</span> embark-consult
  <span class="hljs-symbol">:after</span> (<span class="hljs-name">embark</span> consult))</code></span></pre>


<p>In this scenario, <code>embark-consult</code> remains entirely inactive during startup. Emacs won't evaluate the <code>embark-consult</code> configuration until it has successfully loaded both <code>embark</code> and <code>consult</code>.</p>



<h3>Forcing immediate loading</h3>



<p>Consider this configuration:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">use-package</span> kirigami
  <span class="hljs-symbol">:defer</span> <span class="hljs-literal">t</span>
  <span class="hljs-symbol">:custom</span>
  (<span class="hljs-name">kirigami-show-menu-bar</span> <span class="hljs-literal">t</span>)
  (<span class="hljs-name">kirigami-show-context-menu</span> <span class="hljs-literal">t</span>)
  <span class="hljs-symbol">:init</span>
  (<span class="hljs-name">kirigami-global-mode</span> <span class="hljs-number">1</span>)) <span class="hljs-comment">; This forces immediate loading</span></code></span></pre>


<p>Although <code>:defer t</code> is present, evaluating <code>(kirigami-global-mode 1)</code> during <code>:init</code> triggers the autoload for <code>kirigami-global-mode</code>. The package is therefore loaded during initialization, defeating the intended deferral.</p>



<h4>Loading after the Emacs init phase</h4>



<p>If you prefer to load a package after the init phase, use the <code>:hook</code> keyword:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">use-package</span> kirigami
  <span class="hljs-symbol">:custom</span>
  (<span class="hljs-name">kirigami-show-menu-bar</span> <span class="hljs-literal">t</span>)
  (<span class="hljs-name">kirigami-show-context-menu</span> <span class="hljs-literal">t</span>)
  <span class="hljs-symbol">:hook</span> (<span class="hljs-name">after-init</span> . kirigami-global-mode)) <span class="hljs-comment">; Activate after init</span></code></span></pre>


<p>Using <code>:hook (after-init . kirigami-global-mode)</code> adds the mode to <code>after-init-hook</code>. This ensures the package remains deferred while Emacs evaluates the rest of your init file, guaranteeing the mode is active before the editor is ready for user interaction.</p>



<h2>Global laziness: use-package-always-defer (Not recommended)</h2>



<p>You can configure <code>use-package</code> to automatically assume <code>:defer t</code> for declarations that do not otherwise establish eager loading:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> use-package-always-defer <span class="hljs-literal">t</span>) <span class="hljs-comment">; NOT RECOMMENDED</span></code></span></pre>


<p>This changes the default behavior so that <code>use-package</code> declarations are deferred unless a declaration explicitly requires immediate loading or establishes another loading mechanism.</p>



<p>Note: Global deferral breaks packages that rely on background execution, global hooks, or immediate side effects to function correctly. You will find yourself tracking down silent failures and adding <code>:demand t</code> throughout your initialization file to force eager loading for things like themes, modeline managers, and daemon servers. Instead of relying on a blanket setting that obscures the loading state, setting <code>use-package-always-defer</code> to <code>nil</code> and explicitly deferring packages where appropriate results in a more deterministic setup.</p>



<h2>Diagnostics and verification</h2>



<p>To check whether a package feature has been provided in the current Emacs session, use the <code>featurep</code> function. For example, for the <em>kirigami</em> package:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">featurep</span> 'kirigami)</code></span></pre>


<ul>
<li>Evaluates to <code>nil</code> when the feature has not been provided.</li>



<li>Evaluates to <code>t</code> when the feature has been provided.</li>
</ul>



<h2>Debugging and macro expansion optimization</h2>



<p>If you need to diagnose loading order or macro expansion issues, <code>use-package</code> provides built-in settings to help debugging and performance.</p>



<p>Setting <code>use-package-expand-minimally</code> to <code>t</code> causes the <code>use-package</code> macro to generate less boilerplate code. If you byte-compile your initialization file, this reduces the size of the compiled file and can yield slight performance improvements:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> use-package-expand-minimally <span class="hljs-literal">t</span>)</code></span></pre>


<p>Setting <code>use-package-verbose</code> to <code>t</code> forces <code>use-package</code> to log package loading events to the <code>*Messages*</code> buffer. This is useful for verifying whether deferred packages are loading when expected:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> use-package-verbose <span class="hljs-literal">t</span>)</code></span></pre>


<p>Setting <code>use-package-enable-imenu-support</code> to <code>t</code> causes imenu to see use-package declarations. This allows you to quickly jump to specific <code>use-package</code> blocks using <code>M-x imenu</code>:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> use-package-enable-imenu-support <span class="hljs-literal">t</span>)</code></span></pre>


<p>By default, the <code>:hook</code> keyword automatically appends <code>-hook</code> if you omit it. Setting <code>use-package-hook-name-suffix</code> to <code>nil</code> disables this behavior. Disabling this forces you to write fully qualified hook names (e.g., <code>(python-mode-hook . outline-indent-minor-mode)</code>):</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> use-package-hook-name-suffix <span class="hljs-literal">nil</span>) <span class="hljs-comment">; NOT RECOMMENDED</span></code></span></pre>


<p>Note: It is not recommended to set <code>use-package-hook-name-suffix</code> to <code>nil</code> if you maintain a pre-existing configuration that relies on the default implicit behavior. Modifying this variable globally instantly breaks every existing <code>:hook (mode . function)</code> declaration that omits the <code>-hook</code> suffix. Unless you are building a configuration from scratch or can rewrite and validate every hook assignment in a single pass, leaving the default behavior intact is the most practical decision.</p>



<h2>Startup profiling: measuring the impact</h2>



<p>To benchmark startup optimizations, measure startup at a point later than the end of init-file processing.</p>



<h3>Benchmarking startup</h3>



<p>For an accurate startup-time measurement, read:<br><a href="https://www.jamescherti.com/measuring-emacs-startup-time/"><strong>Measuring Emacs startup time more accurately than the built-in emacs-init-time</strong></a></p>



<h3>Profiling individual packages</h3>



<p><code>use-package</code> can collect loading statistics. Enable statistics gathering after use-package has been loaded but before the use-package declarations are evaluated:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> use-package-compute-statistics <span class="hljs-literal">t</span>)</code></span></pre>


<p>After restarting Emacs, execute: <code>M-x use-package-report</code></p>



<p>This displays a tabulated buffer with timing information for the use-package phases.</p>



<h3>Advanced profiling with benchmark-init</h3>



<p>While <code>use-package-report</code> offers insights into declarative package loading, the third-party package <em>benchmark-init</em> (available on MELPA) provides a more granular view of the entire Emacs startup process. It tracks the time spent loading individual files and executing functions across the whole initialization sequence, allowing you to see exactly where you can influence startup time.</p>



<h2>Related links</h2>



<ul>
<li><a href="https://elpa.gnu.org/packages//doc/use-package.html">The use-package documentation</a></li>



<li><a href="https://www.jamescherti.com/emacs-why-use-setq-instead-setopt/"><strong>Emacs startup - Why setq beats setopt, customize-set-variable, and use-package :custom?</strong></a></li>
</ul>
<div class="yarpp yarpp-related yarpp-related-rss yarpp-template-list">

<h3>Related posts:</h3><ol>
<li><a href="https://www.jamescherti.com/buffer-guardian-el-automatically-save-emacs-buffers/">buffer-guardian.el - Automatically Save Emacs Buffers Without Manual Intervention (When Buffers Lose Focus, Regularly, or After Emacs is Idle)</a></li>
<li><a href="https://www.jamescherti.com/emacs-toggle-a-shell-window-shell-pop/">Easily Toggle an Emacs Terminal with a Single Keystroke using shell-pop (Recently Refactored)</a></li>
<li><a href="https://www.jamescherti.com/easysession-el-persist-restore-emacs-session/">easysession.el: Easily persist and restore Emacs sessions (windows, tab-bar, file buffers, scratch, Dired, narrowing, indirect buffers/clones, Magit buffers...); a robust desktop.el replacement</a></li>
<li><a href="https://www.jamescherti.com/emacs-python-dev-using-eglot-pylsp-ruff-pylint-flake8/">Eglot for Python Development in Emacs: Integrating python-lsp-server (pylsp) with Linters and Formatters</a></li>
<li><a href="https://www.jamescherti.com/emacs-ultisnips-mode-edit-snippets-files/">ultisnips-mode.el - An Emacs major mode for editing Ultisnips snippet files (*.snippets files)</a></li>
<li><a href="https://www.jamescherti.com/essential-emacs-packages/">Must-have Emacs Packages for Efficient Software Development and Text Editing</a></li>
<li><a href="https://www.jamescherti.com/enhancing-performance-samsung-galaxy-phones-tablets/">Improving the Performance of Samsung Galaxy Phones and Tablets</a></li>
</ol>
</div>
</body></html>]]></content>
        <author>
            <name>James Cherti</name>
            <uri>https://www.jamescherti.com</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Irreal: The Vim UserGettingBored Autocmd]]></title>
        <id>https://irreal.org/blog/?p=14058</id>
        <link href="https://irreal.org/blog/?p=14058"/>
        <updated>2026-09-03T15:02:23.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
Evan Hahn has an <a href="https://evanhahn.com/usergettingbored-vim/">amusing post about the Vim UserGettingBored autocmd</a>. Autocmds in Vim are essentially like hook functions in Emacs. They get fired when certain events happen in the editor.
</p>
<p>
Way back before Vim 6.0, Bram Moolenaar added the <code>UserGettingBored</code> autocmd to the Vim documentation. It was a joke and the command didn’t actually do anything—or rather, it was never invoked and didn’t really exist. It was like an Emacs hook that was documented but didn’t actually occur. The documentation said “When the user hits CTRL-C. Just Kidding.” In 2013 the documentation changed to  “When the user presses the same key 42 times. Just kidding! :-)” and, there were various other tweaks along the way. Finally, in 2022 an unofficial plugin was created by Mike Smith that actually implemented the autocmd and did something. You can read what it was in Hahn’s post.
</p>
<p>
None of this matters at all, of course, except to provide evidence that Vim users can be as whimsical as us Emacs users. You probably won’t see anything like this in VSCode. It wouldn’t be “professional”.</p>
</body></html>]]></content>
        <author>
            <name>Irreal</name>
            <uri>https://irreal.org/blog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Dave Pearson: next-gh-pr.el v1.1.0]]></title>
        <id>https://blog.davep.org/2026/09/03/next-gh-pr-el-v1-1-0.html</id>
        <link href="https://blog.davep.org/2026/09/03/next-gh-pr-el-v1-1-0.html"/>
        <updated>2026-09-03T14:17:17.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>A quick little update to
<a href="https://github.com/davep/next-gh-pr.el" target="_blank"><code>next-gh-pr.el</code></a>. Since <a href="https://blog.davep.org/2026/05/19/next-gh-pr-el-v1-0-0.html">writing
the first version</a> I've found it to
be really useful, but I've also bumped into one small issue. Sometimes I'll
create a draft PR before I remember to start the changelog entry, and so
then I want to insert the <em>current</em> PR link rather than the next one.</p>
<p>Now, sure, I can just go and copy the URL and create the link in the
changelog, but where's the fun in that when I can solve the problem in
<a href="https://blog.davep.org/tag/emacs-lisp/">Emacs Lisp</a>? So, with this in mind, I've just updated the
package so that, with a prefix argument to the command, it will include a
link for the latest PR rather than for the next one.</p>
<p>This does, of course, make the name a little less correct, but I'm not going
to rename the package or the command for this one minor difference in
intent.</p></body></html>]]></content>
        <author>
            <name>Dave Pearson</name>
            <uri>https://blog.davep.org</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[TAONAW - Emacs and Org Mode: RSI/Pinky Update]]></title>
        <id>https://taonaw.com/2026/09/02/rsipinky-update.html</id>
        <link href="https://taonaw.com/2026/09/02/rsipinky-update.html"/>
        <updated>2026-09-02T21:36:31.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>I wanted to write a short update to my <a href="https://taonaw.com/2026/08/16/rsi-and-emacs-when-your.html">Emacs pinky / RSI situation</a>.</p>
<p>I am still using my Kinesis Freestyle Edge keyboard. I think I’ve decided on the <a href="https://www.moergo.com">Glove80</a> as a replacement, but the price and the inability to easily return it are giving me second thoughts. Its style is very different, and I know I will have a hard time adjusting. Still as someone pointed out to me it’s not like I’m forced to throw away my current keyboard, which is true. I see myself using my Kinesis for work on the Mac while trying the new Glove80 on Linux for blog posts and more leisure-time typing.</p>
<p>I’ve made a few adjustments, however:</p>
<img src="https://cdn.uploads.micro.blog/96826/2026/kinesis-gaming-split-setup.jpg" width="600" height="351" alt="Auto-generated description: A split ergonomic keyboard with blue backlighting is connected by a cable, positioned on a large owl-themed mousepad alongside a Logitech gaming mouse.">
<p>First, the spread between the two halves is much wider. This is something I was considering when I first got this keyboard, but I didn’t follow through with it. I got used to the wider split within a couple of days. It does make my shoulders feel better and more natural, especially when I’m standing at my desk. With that, I also started using the keyboard’s full tenting capability at all times: I used to have the keyboard flatter when sitting down.</p>
<p>The mouse is in the middle, and since I use it for quick bursts between otherwise typing sessions, it’s fine. When I game, I move it back to the right. I can basically ignore the entire right half of my keyboard while gaming, since most of the action happens on the left.</p>
<p>Gone is the Caps Lock as Ctrl. Caps is just Caps again. I’m still pressing it now and then out of habit, but for the most part, I switched back to using the original Control keys. I try to force myself to use my thumb, which means I have to curve my thumb into my left palm if I use the left side, or I just go all the way to the right and use my Ctrl key there. This is the most difficult part because it’s not just muscle memory: the Ctrl key is out of the way, and it can slow me down.</p>
<p>I also realized that the Windows key, which also acts as the Command key on my Mac, should be way more prominent than this keyboard allows. Thanks to <a href="https://www.popclip.app/">PopClip</a> on the Mac, I don’t use it much for copy-pasting on macOS (and since you have to use your mouse anyway to highlight text in Emacs, having PopClip is just a natural extension that I’m happy to have).</p>
<p>Speaking of the mouse, I know some of you will wrinkle your nose in disgust — but I started using it more in Emacs. Sometimes when I want to select text further down or up from where I am with the mark, it’s just easier to grab it and highlight the text this way instead of using the keyboard. I am also in the process of rediscovering the menus in Emacs (yes, those menus that all of you Emacs gurus love to hide), especially when it comes to reminding myself of useful shortcuts I keep forgetting — like registers, the mark ring, and other org-mode tricks I keep forgetting about.</p>
<p>My pinky still annoys me, but less. I seem to have this habit now of letting it hover over the keyboard instead of resting on the shift key. It’s almost as if I lost my ability to place my left hand correctly and naturally at times. It’s one of the things I hope the Glove80 might help with.</p>
</body></html>]]></content>
        <author>
            <name>TAONAW - Emacs and Org Mode</name>
            <uri>https://taonaw.com/categories/emacs-org-mode/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Irreal: The Newcomers Presets Theme]]></title>
        <id>https://irreal.org/blog/?p=14056</id>
        <link href="https://irreal.org/blog/?p=14056"/>
        <updated>2026-09-02T15:32:19.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
As the result of a long standing discussion on the Emacs devel list, Emacs 31 comes with what amounts to a starter kit called <a href="https://github.com/emacs-mirror/emacs/blob/master/etc/themes/newcomers-presets-theme.el">newcomers-presets</a><sup><a href="https://irreal.org/blog#fn.1">1</a></sup>. The idea is to provide newcomers with a set of useful configurations to help them ease into Emacs. It is, however, much more than training wheels for n00bs. The presets described in it are ones that most experienced Emacs users are already using.
</p>
<p>
I know this because last April Sacha Chua wrote a <a href="https://sachachua.com/blog/2026/04/what-s-in-the-emacs-newcomers-presets-theme/">splendid post that describes what’s in it</a>. I somehow missed it back when she first posted it but she mentioned it in the latest <a href="https://sachachua.com/blog/2026/08/2026-08-31-emacs-news/">Emacs News</a>. As I’ve said before, I don’t usually write about things that she’s already covered in Emacs News but I broke that rule <a href="https://irreal.org/blog/?p=14053">yesterday</a> so I might as well break it again, especially since her post is especially worth your attention.
</p>
<p>
When newcomers-presets was first introduced I didn’t pay too much attention to it but then Prot <a href="https://protesilaos.com/codelog/2026-08-29-emacs-completion-preview-mode/">recommended it for experienced users as well as beginners</a>. The problem was that I was unclear on what exactly it did. Then I saw the reference to Chua’s post. It has a comprehensive list of what it does along with a explanation for each setting.
</p>
<p>
I won’t be using it because I’m not a fan of starter kits for experienced users and because even if I like everything in it today, it could change tomorrow. It’s better, in my opinion, to see what’s in it and add the things you think are useful to your own configuration. Chua includes a handy “Copy code” button with each item to make that easy.
</p>
<div>
<h2>Footnotes: </h2>
<div>
<div class="footdef"><sup><a href="https://irreal.org/blog#fnr.1">1</a></sup> <p></p>
<div class="footpara">
<p>
For some reason it’s described and implemented as a “theme” but actually has little to do with themes as we normally think of them.
</p>
</div>
</div>
</div>
</div>
</body></html>]]></content>
        <author>
            <name>Irreal</name>
            <uri>https://irreal.org/blog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Emacs Redux: Working with Git Worktrees in Magit]]></title>
        <id>https://emacsredux.com/blog/2026/09/02/working-with-git-worktrees-in-magit/</id>
        <link href="https://emacsredux.com/blog/2026/09/02/working-with-git-worktrees-in-magit/"/>
        <updated>2026-09-02T14:28:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>I’ll admit that until fairly recently I had no idea git worktrees existed.
They’ve been part of git for a decade<sup><a href="https://emacsredux.com/#fn:1">1</a></sup> and I never once needed them. Feature
branches did the job just fine - create a branch, do the work, merge it, delete
it, and start over.</p>

<p>What finally introduced me to worktrees was, of all things, AI coding agents.
Tools like Claude Code create a worktree for each task, so several agents (or
several sessions of the same agent) can work on the same repo in parallel
without stepping on each other - or on you. Suddenly my projects directory was
full of <code class="language-plaintext highlighter-rouge">cider-this</code> and <code class="language-plaintext highlighter-rouge">cider-that</code> siblings, and I figured I should
understand what’s actually going on there.</p>



<h2>Worktrees vs Branches</h2>

<p>A branch is just a movable pointer to a commit, and making one is basically
free. The catch is that a repository has a single working directory, so working
on two branches means switching that directory back and forth. You know the
routine: stash your half-done work (or commit it), check out the other branch,
do the thing, check out the first branch again, unstash. It works, but it’s
tedious, and it gets worse when the two branches leave your project in
different build states and every switch means recompiling half the world.</p>

<p>A worktree gives you an additional working directory attached to the same
repository:</p>

<pre><code class="language-shellsession">$ git worktree add ../cider-smart-targeting -b smart-form-targeting
</code></pre>

<p>Now <code class="language-plaintext highlighter-rouge">~/projects/cider-smart-targeting</code> is a full checkout of that branch, while
your main checkout stays exactly where it was. The object database, refs,
stashes and remotes are all shared - a worktree is not a clone, so fetching in
one is fetching in all, and creating one is nearly instant. Each worktree gets
its own <code class="language-plaintext highlighter-rouge">HEAD</code> and index, and git enforces one simple rule: a branch can only
be checked out in one worktree at a time.</p>

<p>When are they worth it? Whenever two things need to happen at the same time: a
long test run on one branch while you work on another, reviewing a PR without
disturbing your half-done work, or - the reason everyone is talking about them
these days - AI agents doing their thing in isolation. The price is pretty
modest: your working files exist on disk more than once, and anything that
isn’t tracked by git (dependencies, build caches, <code class="language-plaintext highlighter-rouge">node_modules</code> and friends)
has to be set up again in each worktree.</p>

<p>If branches have never felt limiting to you, that’s fine - they didn’t for me
either, for over a decade. Worktrees are one of those features you don’t miss
until your workflow changes.</p>

<h2>What About Jujutsu?</h2>

<p>While we’re on the topic of working copies - the most interesting thing
happening in version control right now is
<a href="https://jj-vcs.github.io/jj/">Jujutsu</a> (<code class="language-plaintext highlighter-rouge">jj</code>), a git-compatible VCS that makes
the problem worktrees solve mostly go away. In jj the working copy <em>is</em> a
commit, snapshotted automatically as you work. There’s no staging area and no
stash, because there’s no uncommitted state that could be lost or get in the
way - switching contexts is always safe. It also has proper support for
parallel working directories (<code class="language-plaintext highlighter-rouge">jj workspace</code>), plus an operation log that makes
practically everything undoable. That last bit is part of why the AI agent
crowd has taken an interest in it too.</p>

<p>You can use jj on top of an existing git repository (they call this a colocated
repo), and your colleagues - and Magit - will keep seeing a normal git repo.
I’m still just an observer here, but it’s clearly a project to keep an eye on.</p>

<h2>Worktrees in Magit</h2>

<p>Back to Emacs land. Magit has had worktree support for years, hiding behind <code class="language-plaintext highlighter-rouge">Z</code>:</p>

<table>
  <thead>
    <tr>
      <th>Key</th>
      <th>Command</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Z b</code></td>
      <td><code class="language-plaintext highlighter-rouge">magit-worktree-checkout</code></td>
      <td>Check out an existing branch in a new worktree</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Z c</code></td>
      <td><code class="language-plaintext highlighter-rouge">magit-worktree-branch</code></td>
      <td>Create a new branch and worktree in one go</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Z g</code></td>
      <td><code class="language-plaintext highlighter-rouge">magit-worktree-status</code></td>
      <td>Jump to another worktree’s status buffer</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Z m</code></td>
      <td><code class="language-plaintext highlighter-rouge">magit-worktree-move</code></td>
      <td>Move a worktree</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">Z k</code></td>
      <td><code class="language-plaintext highlighter-rouge">magit-worktree-delete</code></td>
      <td>Delete a worktree</td>
    </tr>
  </tbody>
</table>

<p>The best part is that there’s nothing else to learn. Each worktree gets its own
Magit status buffer, and every Magit command operates on the worktree the
current buffer belongs to. <code class="language-plaintext highlighter-rouge">Z g</code> is the only switching mechanism you need, and
even that is just a shortcut for visiting another status buffer.</p>

<p>A few practical tips from my (admittedly recent) experience:</p>

<ul>
  <li>By default the status buffer doesn’t list your worktrees. Fix that with:</li>
</ul>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="nv">magit-add-section-hook</span> <span class="ss">'magit-status-sections-hook</span>
                        <span class="nf">#'</span><span class="nv">magit-insert-worktrees</span>
                        <span class="no">nil</span> <span class="no">t</span><span class="p">)</span>
</code></pre></div></div>

<p>Now every status buffer shows all worktrees of the repo, and you can hit
  <code class="language-plaintext highlighter-rouge">RET</code> on any of them to jump there.</p>

<ul>
  <li>
    <p>Create worktrees as <em>siblings</em> of the main checkout with descriptive names
(<code class="language-plaintext highlighter-rouge">cider-smart-targeting</code> next to <code class="language-plaintext highlighter-rouge">cider</code>), not nested inside it - nesting
confuses <code class="language-plaintext highlighter-rouge">grep</code>, <code class="language-plaintext highlighter-rouge">find</code> and plenty of other tools.</p>
  </li>
  <li>
    <p>Magit’s branch selection annotates branches that are checked out in another
worktree with the worktree’s path, and refuses to check them out a second
time (that’s git’s rule, not Magit being difficult). If you ever wondered why
a branch “won’t check out”, that’s usually why.</p>
  </li>
  <li>
    <p>Each worktree is its own project as far as <code class="language-plaintext highlighter-rouge">project.el</code> (or Projectile) is
concerned, so project switching, per-project buffers and search all work
naturally.</p>
  </li>
  <li>
    <p>Anything that’s not tracked by git doesn’t come along for the ride. You’ll
have to install dependencies once per worktree and start with a cold build
cache. For Elisp that costs you nothing; for a big JVM or JS project it’s the
main downside of the whole approach.</p>
  </li>
  <li>
    <p>When you’re done with a worktree, delete it with <code class="language-plaintext highlighter-rouge">Z k</code> (or with
<code class="language-plaintext highlighter-rouge">git worktree remove</code> from the command line). If you just delete the
directory by hand, <code class="language-plaintext highlighter-rouge">git worktree prune</code> will clean up the leftover
bookkeeping.</p>
  </li>
</ul>

<h2>Closing Thoughts</h2>

<p>These days my workflow with agents usually looks like this: an agent does its
work in a worktree, I review the changes there in Magit (often while another
task is running in a second worktree), and the worktree goes away once the
branch is merged. Maybe one day I’ll find other uses for worktrees - we’ll see.</p>

<p>Are you using git worktrees - and did you discover them the same roundabout way
I did? I’d love to hear about it in the comments!</p>

<p>That’s all I have for you today. Keep hacking (in parallel)!</p>

<div class="footnotes">
  <ol>
    <li>
      <p>They were introduced in git 2.5, released all the way back in July 2015.&nbsp;<a href="https://emacsredux.com/#fnref:1">↩</a></p>
    </li>
  </ol>
</div></body></html>]]></content>
        <author>
            <name>Emacs Redux</name>
            <uri>https://emacsredux.com/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Tim Heaney: Alpine Linux]]></title>
        <id>https://oylenshpeegul.gitlab.io/blog/posts/20260902/</id>
        <link href="https://oylenshpeegul.gitlab.io/blog/posts/20260902/"/>
        <updated>2026-09-02T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body>I just installed Alpine Linux on an old laptop. Works great!
 Last year, I complained about all of the lightweight Linux distributions being based on Debian. Alpine is not based on Debian and is very lightweight indeed; it uses busybox in place of many GNU utilities, musl libc in place of glibc, and openrc in place of systemd. Alpine is so small that lots of folks use it for containers and things.</body></html>]]></content>
        <author>
            <name>Tim Heaney</name>
            <uri>https://oylenshpeegul.gitlab.io/blog/tags/emacs/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Sacha Chua: Emacs Chat 30: Fabrice Niessen (en français, partie 2)]]></title>
        <id>https://sachachua.com/blog/2026/08/27-aout-emacs-chat-fabrice-niessen-en-francais-partie-2/</id>
        <link href="https://sachachua.com/blog/2026/08/27-aout-emacs-chat-fabrice-niessen-en-francais-partie-2/"/>
        <updated>2026-09-01T15:58:31.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
Nous avons parlé en direct avec <a href="https://github.com/fniessen">Fabrice Niessen</a> d'Emacs pour la deuxième fois. <a href="https://github.com/fniessen/emacs-chat-sacha-chua">Voici ses notes</a>.
</p>

<p>
</p><div class="yt-video"><a href="https://youtube.com/live/4m0G_ASLUXY">Watch on YouTube</a></div>
<p></p>

<p>
Correction : J'ai voulu dire <a href="https://speaches.ai/">Speaches</a> (un serveur de reconnaissance vocale) au lieu de speechd (un serveur de synthèse vocale)
</p>

<p>
Voici notre conversation précédente : <a href="https://sachachua.com/blog/2026/08/13-aout-emacs-chat-fabrice-niessen/">Emacs Chat 28: Fabrice Niessen (en français)</a>
</p>
<div class="outline-3">
<h3><a href="https://sachachua.com/blog/feed/index.xml#emacs-chat-30-fabrice-niessen-en-fran-ais-partie-2-chapitres">Chapitres</a></h3>
<div class="outline-text-3">
<p>
</p><ul>
<li><span class="audio-time">0:00</span> Introduction</li>

<li><span class="audio-time">3:00</span> gptel</li>
<li><span class="audio-time">13:42</span> diff</li>
<li><span class="audio-time">17:52</span> gptel-commit-message</li>
<li><span class="audio-time">23:30</span> docstrings</li>
<li><span class="audio-time">24:03</span> hs-minor-mode - hideshow</li>
<li><span class="audio-time">31:03</span> Super-whisper</li>
<li><span class="audio-time">41:21</span> Dotfiler</li>
<li><span class="audio-time">59:12</span> Enseignement d'Emacs</li>
</ul>

<p></p>
</div>
</div>
<div class="outline-3">
<h3><a href="https://sachachua.com/blog/feed/index.xml#emacs-chat-30-fabrice-niessen-en-fran-ais-partie-2-la-transcription-l-g-rement-corrig-e">La transcription légèrement corrigée</a></h3>
<div class="outline-text-3">
<a name="ID-emacs-chat-30-transcript"></a>Details
<p>
</p><div class="full-transcript"><p></p><div class="transcript-heading"><span class="audio-time">0:00</span> <strong>Introduction
</strong></div><p></p><span class="audio-time caption"><strong>Prot:</strong>  Tu peux prendre le lien correct cette fois.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Ah, voilà, je suis prêt avec.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Ah, ok.</span> <span class="audio-time caption">Ok.</span> <span class="audio-time caption">Nous sommes en direct.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Ah, nous sommes en direct.</span> <span class="audio-time caption">Très bien.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Donc, je vais passer à vos notes.</span> <span class="audio-time caption">Bonjour à toutes et à tous et bienvenue au 30e</span> <span class="audio-time caption">épisode d'Emacs Chat.</span> <span class="audio-time caption">C'est la suite de notre conversation avec Fabrice Nissen.</span> <span class="audio-time caption">Merci de nouveau Fabrice d'être là et merci</span> <span class="audio-time caption">également à Prot de nous rejoindre.</span> <span class="audio-time caption">Donc, on continue.</span> <span class="audio-time caption">Lors de la conversation précédente, nous avons</span> <span class="audio-time caption">parlé de ton expérience sur Windows et sur WSL,</span> <span class="audio-time caption">des thèmes de publication vers HTML ou vers PDF,</span> <span class="audio-time caption">de ton kit de configuration Leuven, des petites améliorations</span> <span class="audio-time caption">et des automatisations et des formations et des</span> <span class="audio-time caption">cours particuliers en français, en anglais, en</span> <span class="audio-time caption">néerlandais et en espagnol.</span> <span class="audio-time caption">C'est incroyable, ça!</span> <span class="audio-time caption">Du coup, si ça ne te dérange pas, j'essaie d'en</span> <span class="audio-time caption">savoir plus sur ton flux de travail, puis repasser</span> <span class="audio-time caption">à une discussion sur l'enseignement d'Emacs,</span> <span class="audio-time caption">de nombreux changements et de grandes</span> <span class="audio-time caption">incertitudes à l'époque de l'IA.</span> <span class="audio-time caption">Qu'en penses-tu?</span> <span class="audio-time caption">Depuis notre conversation, tu as ajouté beaucoup</span> <span class="audio-time caption">de fonctions, d'outils, de préréglages (presets)</span> <span class="audio-time caption">de gptel à ta configuration.</span> <span class="audio-time caption">C'est un sujet très tendance</span> <span class="audio-time caption">YouTube a évidemment beaucoup de video</span> <span class="audio-time caption">de ce genre en anglais, mais pas encore en français, je pense.</span> <span class="audio-time caption">Peux-tu nous montrer ton flux de travail avec l'IA sur Emacs?</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Volontiers.  Merci pour l'invitation et l'image bouge.</span> <span class="audio-time caption">Merci pour l'invitation pour cette trentième fois.</span> <span class="audio-time caption">Donc, je vais partager mon écran.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Merci.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Alors, je tape share, voilà.</span> <span class="audio-time caption">Et donc, ce sera celui-ci.</span> <span class="audio-time caption">Est-ce que vous voyez mon écran ?</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Oui, je peux voir ton écran.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Ok, très bien.</span> <span class="audio-time caption">Très bien.</span> <p></p><div class="transcript-heading"><span class="audio-time">3:00</span> <strong>gptel</strong></div><p></p><span class="audio-time caption"><strong>Fabrice:</strong> Donc en fait, sur gptel, j'utilise pas encore</span> <span class="audio-time caption">beaucoup, beaucoup, beaucoup, mais de plus en plus.</span> <span class="audio-time caption">Je suis aussi un peu novice, un peu, mais j'ai</span> <span class="audio-time caption">effectivement, et récemment d'ailleurs, fait</span> <span class="audio-time caption">quelques petites améliorations de layout au standard.</span> <span class="audio-time caption">Donc j'ai, par exemple, utilisé le raccourci</span> <span class="audio-time caption"><code>C-&lt;F1&gt;</code> pour lancer…</span> <span class="audio-time caption">Je n'ai pas lancé le chat.</span> <span class="audio-time caption">Je vois que je n'ai pas ma dernière configuration.</span> <span class="audio-time caption">C'est peut-être l'occasion aussi de parler,</span> <span class="audio-time caption">juste un petit break.</span> <span class="audio-time caption">Vous voyez que j'utilise Helm.</span> <span class="audio-time caption">Je ne sais pas ce que vous utilisez vous.</span> <span class="audio-time caption">Je suis extrêmement content de Helm, ce qui me</span> <span class="audio-time caption">permet de taper.</span> <span class="audio-time caption">Je ne dois plus savoir où se trouve un fichier,</span> <span class="audio-time caption">dans quel répertoire.</span> <span class="audio-time caption">Il suffit que je connaisse quelques morceaux de son nom.</span> <span class="audio-time caption">Et je vais retrouver directement le fichier ou le</span> <span class="audio-time caption">buffer actif.</span> <span class="audio-time caption">Donc ici c'est gptel et ce sera le point el.</span> <span class="audio-time caption">Il est là.</span> <span class="audio-time caption">Donc ici, je vais lancer et je vais faire <code>vb</code></span> <span class="audio-time caption">rapidement pour le keychord <code>eval-buffer</code>.</span> <span class="audio-time caption">Voilà, donc il est évalué.</span> <span class="audio-time caption">Donc je vais avoir un look un peu différent</span> <span class="audio-time caption">maintenant si je recommence.</span> <span class="audio-time caption">Voilà, <code>C-&lt;F1&gt;</code>.</span> <span class="audio-time caption">Donc on peut lui dire bonjour.</span> <span class="audio-time caption"><code>C-c C-c</code> pour envoyer.</span> <span class="audio-time caption">Voilà.</span> <span class="audio-time caption">Et donc ici il répond.</span> <span class="audio-time caption">Donc une petite utilisation que j'ai faite, c'est</span> <span class="audio-time caption">gptel-highlight-mode qui permet de mettre la</span> <span class="audio-time caption">réponse avec un certain fond et une petite barre</span> <span class="audio-time caption">ici dans la frange pour bien identifier la</span> <span class="audio-time caption">différence entre les questions et les réponses.</span> <span class="audio-time caption">Je peux lui demander d'écrire</span> <span class="audio-time caption">Un exemple de code Python.</span> <span class="audio-time caption">S'il fait comme d'habitude, il va faire la suite</span> <span class="audio-time caption">de Fibonacci.</span> <span class="audio-time caption">Voilà.</span> <span class="audio-time caption">Et donc, qu'est-ce qu'il a fait ?</span> <span class="audio-time caption">Ah non, les nombres premiers.</span> <span class="audio-time caption">Ici, c'est quelque chose que je commence à</span> <span class="audio-time caption">utiliser de plus en plus, le buffer de</span> <span class="audio-time caption">conversation gptel.</span> <span class="audio-time caption">Une petite customisation que j'ai faite.</span> <span class="audio-time caption">D'ailleurs, j'ai envoyé un issue à l'auteur de</span> <span class="audio-time caption">gptel pour éventuellement qu'il l'introduise</span> <span class="audio-time caption">directement dans...</span> <span class="audio-time caption">Dans gptel, c'est le fait de recoloriser les</span> <span class="audio-time caption">codes blocs dans les réponses en utilisant la</span> <span class="audio-time caption">couleur que j'ai dans mon thème pour Org Mode.</span> <span class="audio-time caption">Donc ici, c'est exactement comme dans un fichier</span> <span class="audio-time caption">Org Mode.</span> <span class="audio-time caption">J'ai le fond jaune pour le code et les</span> <span class="audio-time caption">délimiteurs ici, de début et de fin, en gris.</span> <span class="audio-time caption">Et ça, c'est très important, je trouve, pour</span> <span class="audio-time caption">mettre en évidence comme il faut le code bloc.</span> <span class="audio-time caption">Et le fait, bon ici ce n'est pas de l'Emacs Lisp,</span> <span class="audio-time caption">mais si j'avais demandé, si je pouvais demander</span> <span class="audio-time caption">de faire la version, donnez-moi la version de</span> <span class="audio-time caption">l'Emacs Lisp.</span> <span class="audio-time caption"><code>C-c C-c</code>.</span> <span class="audio-time caption">Et l'avantage évidemment de faire ici, donc à la</span> <span class="audio-time caption">fin de la réponse, il y a un scroll automatique</span> <span class="audio-time caption">vers le bas.</span> <span class="audio-time caption">Ah d'accord.</span> <span class="audio-time caption">Et ce qu'il fait aussi, c'est qu'il fait un</span> <span class="audio-time caption">formatting pour que ça reste moins de 80</span> <span class="audio-time caption">caractères.</span> <span class="audio-time caption">Et donc ici, l'avantage, c'est que c'est dans</span> <span class="audio-time caption">Emacs et que c'est l'Emacs Lisp.</span> <span class="audio-time caption">Je peux directement faire <code>C-x C-e</code>. Et je</span> <span class="audio-time caption">pourrais tester les fonctions.</span> <span class="audio-time caption">Je pourrais faire ici.</span> <span class="audio-time caption">Ça me permet d'éviter de faire des copier-coller</span> <span class="audio-time caption">entre ChatGPT ou Copilot ou n'importe lequel.</span> <span class="audio-time caption">Ici j'ai tout directement là dedans.</span> <span class="audio-time caption">C'est extrêmement pratique.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Tu as aussi des fonctions pour ajouter</span> <span class="audio-time caption">des contextes, des fonctions, d'autres choses</span> <span class="audio-time caption">dans ta configuration.</span> <span class="audio-time caption">Au lieu d'utilisation de conversation buffer, tu</span> <span class="audio-time caption">as aussi utilisé les fonctionnalités gptel dans</span> <span class="audio-time caption">ton code.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Il y a le rewrite que j'utilise</span> <span class="audio-time caption">parfois, ou alors pour répondre avec le contexte,</span> <span class="audio-time caption">il y a notamment le add context ou add context file.</span> <span class="audio-time caption">Je n'ai pas encore beaucoup joué avec, donc je</span> <span class="audio-time caption">sais qu'on peut aller dans Dired et puis faire</span> <span class="audio-time caption">gptel-add-file, add context file, je crois.</span> <span class="audio-time caption">Et je l’ai déjà bindé sur <code>C-c g</code>, donc tout ce</span> <span class="audio-time caption">qui est <code>C-c g</code> et c’est gptel.</span> <span class="audio-time caption">J’ai déjà utilisé le raccourci avec un petit <code>a</code>,</span> <span class="audio-time caption">donc <code>C-c g a</code> pour ajouter du contexte.</span> <span class="audio-time caption">Mais je ne l'ai pas encore vraiment utilisé, donc</span> <span class="audio-time caption">je n'ai pas encore beaucoup d'expérience avec.</span> <span class="audio-time caption">Par contre, je fais parfois dans des fichiers, je</span> <span class="audio-time caption">peux faire, si je prends un fichier Org, on peut</span> <span class="audio-time caption">aller dans le fichier ici avec mes petites notes</span> <span class="audio-time caption">d'aujourd'hui.</span> <span class="audio-time caption">Ça pourrait m'arriver ici de dire je veux</span> <span class="audio-time caption">traduire ces deux paragraphes en anglais par</span> <span class="audio-time caption">exemple.</span> <span class="audio-time caption">Je vais les sélectionner.</span> <span class="audio-time caption">Je vais faire <code>C-u C-c RET</code> pour avoir le</span> <span class="audio-time caption">menu de gptel.</span> <span class="audio-time caption">Là, je vais changer la directive puisque la</span> <span class="audio-time caption">directive ici c'est tu es un assistant, etc.</span> <span class="audio-time caption">Je vais changer la directive.</span> <span class="audio-time caption">Je vais en fait utiliser le <code>S</code>, Set System Message.</span> <span class="audio-time caption">Là, j'ai une série de prompt, en fait, de system</span> <span class="audio-time caption">prompt, de directives, ça s'appelle, sous gptel.</span> <span class="audio-time caption">Donc, une série de directives, de prompt qui sont prêts.</span> <span class="audio-time caption">Mais ici, je vais faire un spécifique.</span> <span class="audio-time caption">Donc, je vais encore faire une fois <code>s</code>. Et là, je</span> <span class="audio-time caption">vais taper, par exemple, traduit.</span> <span class="audio-time caption">Là, quand on a fini, on peut faire <code>C-c C-c</code>.</span> <span class="audio-time caption">Ici, on voit la directive qui est là.</span> <span class="audio-time caption">Pour l'instant, si je ne change rien, il va</span> <span class="audio-time caption">envoyer ma sélection et il va insérer la réponse.</span> <span class="audio-time caption">Là, je ne vois pas très bien.</span> <span class="audio-time caption">Il faut que je déplace un truc.</span> <span class="audio-time caption">Il va insérer la réponse à la fin.</span> <span class="audio-time caption">Je pourrais faire ça, par exemple, pour voir</span> <span class="audio-time caption">comment il a traduit.</span> <span class="audio-time caption">Comme ça, je garde au-dessus l'input.</span> <span class="audio-time caption">Je vais faire effectivement <code>RET</code> ici.</span> <span class="audio-time caption">Parfois, c'est un peu compliqué à lire.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Qu'est-ce qu'il m'a fait ?</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  On dirait qu'il a pris plus de</span> <span class="audio-time caption">lignes.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Il y a un problème des démos en direct.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Je vais bien le refaire.</span> <span class="audio-time caption"><code>C-u C-c RET</code>.</span> <span class="audio-time caption">Je vais bien sectionner ma région.</span> <span class="audio-time caption">La directive est là.</span> <span class="audio-time caption">Donc, je vais faire ici Enter.</span> <span class="audio-time caption">Voilà.</span> <span class="audio-time caption">Donc j'ai vu que l'input était, la section était</span> <span class="audio-time caption">toujours sélectionnée, donc j’ai fait <code>C-w</code> pour</span> <span class="audio-time caption">l'entrée, et puis j'ai gardé ce qu'il y a.</span> <span class="audio-time caption">Donc ici, voilà, typiquement, il a fait le job</span> <span class="audio-time caption">facilement, donc je suis nouveau restant dans</span> <span class="audio-time caption">Directement dans Emacs, on fait des copies-coller</span> <span class="audio-time caption">avec d'autres interfaces externes.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Je vois que tu peux aussi remplacer la</span> <span class="audio-time caption">sélection avec les résultats ou peut-être</span> <span class="audio-time caption">rediriger l'output à un autre tampon pour</span> <span class="audio-time caption">comparaison.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Exact.</span> <span class="audio-time caption">Donc, je n'ai pas encore tout utilisé, mais je</span> <span class="audio-time caption">pourrais ici essayer.</span> <span class="audio-time caption">On va essayer.</span> <span class="audio-time caption">On va traduire en français, par exemple.</span> <span class="audio-time caption">Donc, j'ai bien sélectionné.</span> <span class="audio-time caption">Je vais faire <code>C-u C-c RET</code> pour avoir de</span> <span class="audio-time caption">nouveau le menu.</span> <span class="audio-time caption">Ou alors, M-x, j'ai fait tel menu.</span> <span class="audio-time caption">Donc là, je vais changer et je vais mettre la</span> <span class="audio-time caption">directive 2 fois S. Et cette fois-ci, je vais</span> <span class="audio-time caption">traduire en français.</span> <span class="audio-time caption"><code>C-c C-c</code>, voilà.</span> <span class="audio-time caption">Et donc, je vais mettre ici le respond in place.</span> <span class="audio-time caption">Et donc, en théorie, il va écraser ma sélection</span> <span class="audio-time caption">par sa réponse.</span> <span class="audio-time caption">Donc, je vais faire le <code>i</code> ici.</span> <span class="audio-time caption">Et donc, en fait, il faut toujours lire la</span> <span class="audio-time caption">dernière ligne ici.</span> <span class="audio-time caption">On voit ce qu'il va faire.</span> <span class="audio-time caption">C'est un résumé de ce qu'il va faire en fonction</span> <span class="audio-time caption">des options qu'on a choisi.</span> <span class="audio-time caption">Donc ici, quand je vais faire ret, quand je vais</span> <span class="audio-time caption">faire return, il va remplacer la sélection avec</span> <span class="audio-time caption">la réponse.</span> <span class="audio-time caption">Donc je fais <code>RET</code>.</span> <span class="audio-time caption">Ah, et là j'ai peur.</span> <span class="audio-time caption">The conversation must end with a user message.</span> <span class="audio-time caption">Qu'est-ce qu'on va faire ?</span> <span class="audio-time caption">On va rester, on ne sait jamais.</span> <span class="audio-time caption"><code>C-x C-x C-u RET</code>…</span> <span class="audio-time caption">Non, ce n'était pas ça.</span> <span class="audio-time caption"><code>C-x u</code>. <code>C-x u</code>. Voilà.</span> <span class="audio-time caption"><code>C-x C-x</code> <code>C-u RET</code></span> <span class="audio-time caption">Ben si, c'est ça.</span> <span class="audio-time caption">Non, <code>C-u C-c RET</code>.</span> <span class="audio-time caption">Il y a trop de raccourcis.</span> <span class="audio-time caption">Je fais la sélection, voilà.</span> <span class="audio-time caption"><code>C-u C-c RET</code>.</span> <span class="audio-time caption">J'ai mon menu, j'ai ma directive en haut qui est bonne.</span> <span class="audio-time caption">Je vais faire le <code>i</code>, Replace, la sélection du</span> <span class="audio-time caption">response, <code>RET</code>.</span> <span class="audio-time caption">Bon, j'ai l'erreur.</span> <span class="audio-time caption">Donc là, il faudrait que je regarde un peu plus.</span> <span class="audio-time caption">Je n'ai pas beaucoup utilisé ça.</span> <span class="audio-time caption">Et en fait, justement, sur le rewrite, il faut</span> <span class="audio-time caption">que j'utilise aussi un peu plus pour du texte et</span> <span class="audio-time caption">pour du code.</span> <span class="audio-time caption">Et je sais qu'il y a plusieurs possibilités après.</span> <p></p><div class="transcript-heading"><span class="audio-time">13:42</span> <strong>diff</strong></div><p></p><span class="audio-time caption"><strong>Fabrice:</strong> Il y a aussi moyen de dire, il va remplacer en</span> <span class="audio-time caption">théorie par le nouveau texte, mais il y a moyen</span> <span class="audio-time caption">de montrer aussi son changement sous forme de</span> <span class="audio-time caption">diff ou de ediff.</span> <span class="audio-time caption">Donc il y a moyen de dire accepte.</span> <span class="audio-time caption">J’ai vu qu’après on peut faire <code>C-c C-a</code></span> <span class="audio-time caption">pour accepte, <code>C-c C-e</code> pour ediff,</span> <span class="audio-time caption"><code>C-c C-d</code> pour diff.</span> <span class="audio-time caption">Et donc il peut rouvrir différents buffers pour</span> <span class="audio-time caption">bien visualiser la différence.</span> <span class="audio-time caption">Si c'est une traduction, tout serait changé.</span> <span class="audio-time caption">Et si on modifie du code, par exemple, en disant</span> <span class="audio-time caption">rajoute-moi un paramètre, on a envie de voir</span> <span class="audio-time caption">vraiment la différence et de sûr qu'il n'a pas</span> <span class="audio-time caption">fait plus de choses que ce qu'on a demandé.</span> <span class="audio-time caption">Donc il y a moyen de faire un diff ou un ediff après.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  C'est peut-être mieux comme ça avec Ediff.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Oui, moi j'aime beaucoup Ediff.</span> <span class="audio-time caption">D'ailleurs, c'est quelque chose, je ne crois pas</span> <span class="audio-time caption">que je l'ai montré l'autre fois.</span> <span class="audio-time caption">Donc ici, moi j’ai <code>C-&lt;f9&gt;</code> chez moi, c’est
privé,</span> <span class="audio-time caption">pour lancer <code>vc-dir</code> sans me poser de questions sur</span> <span class="audio-time caption">quel est le répertoire qu'on veut analyser.</span> <span class="audio-time caption">Donc ici, je vais faire <code>C-&lt;f9&gt;</code>.</span> <span class="audio-time caption">Donc il me lance vc-dir sur le répertoire dans</span> <span class="audio-time caption">lequel je me trouvais.</span> <span class="audio-time caption">On voit que j'ai fait une modification dans le</span> <span class="audio-time caption">fichier readme.</span> <span class="audio-time caption">Si je fais égal, on voit un diff unifié avec les</span> <span class="audio-time caption">plus et les moins en dessous.</span> <span class="audio-time caption">Il faut toujours relire avant de committer les</span> <span class="audio-time caption">diffs parce que là il y a une ligne qui n'était</span> <span class="audio-time caption">pas prévue d'être committée en imaginant que je</span> <span class="audio-time caption">devais faire le commit.</span> <span class="audio-time caption">Je vais d'ailleurs effacer cette ligne.</span> <span class="audio-time caption">Ce qui est pratique ici dans tout ça, c'est que</span> <span class="audio-time caption">j'ai fait une modification, j'ai sauvé.</span> <span class="audio-time caption">Ce buffer-ci est le diff.</span> <span class="audio-time caption">Je n'ai pas besoin de retourner dans le vc-dir et</span> <span class="audio-time caption">de refaire un égal.</span> <span class="audio-time caption">Je peux faire <code>g</code>, faire refresh, il recalcule le</span> <span class="audio-time caption">contenu.</span> <span class="audio-time caption">Donc ici, j’ai fait <code>g</code> et simplement, il a</span> <span class="audio-time caption">recalculé le diff.</span> <span class="audio-time caption">Et je ne vois plus que le paragraphe qu'on devait</span> <span class="audio-time caption">traduire, qui a été effacé.</span> <span class="audio-time caption">Pour l'instant, c'est une possibilité.</span> <span class="audio-time caption">Et l'autre possibilité à partir de vc-dir.</span> <span class="audio-time caption">Pour l'instant, je n'utilise pas encore Magit</span> <span class="audio-time caption">parce que j'ai eu pendant des années des</span> <span class="audio-time caption">répertoires, des repos qui étaient sous SVN,</span> <span class="audio-time caption">subversion.</span> <span class="audio-time caption">Certains étaient déjà sous Git.</span> <span class="audio-time caption">Je voulais une seule interface pour gérer tous</span> <span class="audio-time caption">mes repositories.</span> <span class="audio-time caption">Et donc, j'ai tout construit sur vc-dir qui marche</span> <span class="audio-time caption">très bien.</span> <span class="audio-time caption">Voilà, je suis sûr que Magit, pour Git, est</span> <span class="audio-time caption">plus sophistiquée, plus jolie à regarder, sans doute.</span> <span class="audio-time caption">Mais en fait, je fais tout mon boulot ici avec vc-dir.</span> <span class="audio-time caption">Donc, si j'avais égal, j'ai un diff unifié.</span> <span class="audio-time caption">Ça, c'est standard, je crois.</span> <span class="audio-time caption">Si je fais grand <code>E</code>, ça, c’est pas standard.</span> <span class="audio-time caption">Ça m'appelle ediff.</span> <span class="audio-time caption">Donc là, directement, j'ai ediff, puis j'ai plus</span> <span class="audio-time caption">qu'à faire next.</span> <span class="audio-time caption">Voilà, next.</span> <span class="audio-time caption">Bon, j'ai qu'un changement ici.</span> <span class="audio-time caption">Ce paragraphe là qui a disparu.</span> <span class="audio-time caption">Donc ça c'est très pratique, directement pouvoir</span> <span class="audio-time caption">lancer Ediff à partir d'un fichier modifié.</span> <span class="audio-time caption">Et alors une petite chose que j'ai récemment</span> <span class="audio-time caption">rajoutée dans Git, c'est donc ici je viens</span> <span class="audio-time caption">de refaire un égal pour avoir un Ediff unifié.</span> <span class="audio-time caption">Donc ici j'ai le</span> <span class="audio-time caption">Le diff unifié où j'ai ce paragraphe-là qui est apparu.</span> <span class="audio-time caption">En fait, depuis n'importe quel diff, donc output</span> <span class="audio-time caption">qui est sous forme de diff avec des plus et des moins,</span> <span class="audio-time caption">Ici, c'est à partir de vc-diff.</span> <span class="audio-time caption">Ça pourrait être le résultat d'un Git diff dans</span> <span class="audio-time caption">un buffer.</span> <span class="audio-time caption">Ça pourrait être dans Magit.</span> <span class="audio-time caption">Je peux lancer la touche avec <code>w</code>, ça marche.</span> <span class="audio-time caption">Ça m'a lancé la génération d'un message de commit</span> <span class="audio-time caption">via gptel.</span> <span class="audio-time caption">J'ai écrit une petite fonction qui va envoyer</span> <span class="audio-time caption">tout ce buffer-là.</span> <p></p><div class="transcript-heading"><span class="audio-time">17:52</span> <strong>gptel-commit-message</strong></div><p></p><span class="audio-time caption"><strong>Fabrice:</strong> Ça envoie le buffer en entier à gptel.</span> <span class="audio-time caption">En lui demandant de m'écrire un message de commit.</span> <span class="audio-time caption">C'est gptel-commit-message.</span> <span class="audio-time caption">Je l'ai mis sous GitHub.</span> <span class="audio-time caption">Il génère un message de commit à partir d'un</span> <span class="audio-time caption">fichier, d'un buffer diff.</span> <span class="audio-time caption">Il envoie tout ça et donc il a été généré</span> <span class="audio-time caption">n'importe comment, donc ce n'est pas lié à Magit.</span> <span class="audio-time caption">Parce qu'il y a beaucoup, il y a plusieurs</span> <span class="audio-time caption">solutions qui existent déjà dans MELPA et c'est</span> <span class="audio-time caption">lié souvent à Magit.</span> <span class="audio-time caption">Ou à Git, en fait, il faut avoir stagé les</span> <span class="audio-time caption">fichiers.</span> <span class="audio-time caption">Moi, je n'ai pas spécialement envie de mettre des</span> <span class="audio-time caption">fichiers, de les stager dans Git.</span> <span class="audio-time caption">J'ai envie de pouvoir simplement sélectionner</span> <span class="audio-time caption">moi-même ici dans l'interface un, deux, trois</span> <span class="audio-time caption">fichiers et regarder la différence pour ces</span> <span class="audio-time caption">fichiers-là.</span> <span class="audio-time caption">Génère-moi un message pour ces fichiers.</span> <span class="audio-time caption">Ici, dans le package, qui est tout petit, il fait</span> <span class="audio-time caption">une fonction.</span> <span class="audio-time caption">Il y a juste une fonction.</span> <span class="audio-time caption">Il y a le nom du buffer et la fonction.</span> <span class="audio-time caption">Derrière, je l’ai mappé sur <code>w</code>. Quand je suis dans</span> <span class="audio-time caption">le diff-mode-map, dans vc-dir,</span> <span class="audio-time caption">Je tape <code>w</code> pour write.</span> <span class="audio-time caption">Ça me gênait le fichier de ce message.</span> <span class="audio-time caption">Donc, si je reviens sur ton changement, voilà.</span> <span class="audio-time caption">Donc, j'avais ici, j'avais fait W à partir d'un</span> <span class="audio-time caption">buffer diff.</span> <span class="audio-time caption">Ça a envoyé via gptel, ça a envoyé le diff, ça a</span> <span class="audio-time caption">récupéré un message de commit, ça l'a mis dans le</span> <span class="audio-time caption">presse-papier et ça m'a ouvert un buffer, donc ça</span> <span class="audio-time caption">a rajouté un buffer à côté où je pouvais le voir.</span> <span class="audio-time caption">Je vais le refaire ici.</span> <span class="audio-time caption">Donc ça envoie, voilà.</span> <span class="audio-time caption">Et donc on voit, ça ouvre un buffer avec le</span> <span class="audio-time caption">message en bas.</span> <span class="audio-time caption">Et en fait, je peux très bien me dire, tiens, en</span> <span class="audio-time caption">fait, ce message-là, il ne me plaît pas trop.</span> <span class="audio-time caption">Puisqu'en fait, on a chaque fois des réponses</span> <span class="audio-time caption">différentes.</span> <span class="audio-time caption">Donc je peux très bien refaire W en haut.</span> <span class="audio-time caption">Il va réenvoyer le truc.</span> <span class="audio-time caption">Cette fois-ci, ça ressemble assez fort.</span> <span class="audio-time caption">Je vais le refaire.</span> <span class="audio-time caption">Il change un petit peu, il n'y a pas beaucoup de</span> <span class="audio-time caption">changement ici parce que ce n'est pas le même texte.</span> <span class="audio-time caption">Donc, remove personal introduction from readme.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Et il a copié aussi sur le kill ring.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Oui, exactement.</span> <span class="audio-time caption">Donc, maintenant, en fait, je n'ai plus qu'à</span> <span class="audio-time caption">refaire ctrl .</span> <span class="audio-time caption">Je suis déjà là-dessus.</span> <span class="audio-time caption">Je n'ai plus qu'à faire dans V, c'est dire, next</span> <span class="audio-time caption">action, c'est V. Pour dire, fais l'action</span> <span class="audio-time caption">suivante qui a du sens.</span> <span class="audio-time caption">Ici, puisque j'ai un fichier modifié, l'action</span> <span class="audio-time caption">suivante qui a du sens, c'est de committer.</span> <span class="audio-time caption">Donc, il m'ouvre un buffer pour pouvoir mettre</span> <span class="audio-time caption">mon message de commit.</span> <span class="audio-time caption">Donc, je fais simplement <code>C-y</code>.</span> <span class="audio-time caption">Et éventuellement, je vais un peu le modifier.</span> <span class="audio-time caption"><code>C-c C-c</code>, c’est envoyé.</span> <span class="audio-time caption">Et donc, c'est committé.</span> <span class="audio-time caption">Ça, j'utilise depuis quelques mois quand même, je</span> <span class="audio-time caption">dirais depuis 4, 5, 6 mois.</span> <span class="audio-time caption">Depuis cette période-là, je fais tous mes</span> <span class="audio-time caption">messages de commit avec gptel, l'intelligence artificielle.</span> <span class="audio-time caption">Et c'est merveilleux parce que ça a augmenté la</span> <span class="audio-time caption">qualité de mes messages de commit.</span> <span class="audio-time caption">C'est indéniable.</span> <span class="audio-time caption">Avant, je mettais souvent dans mes fichiers à moi « update ».</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Moi aussi.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Et en plus, non seulement j'ai un beau message</span> <span class="audio-time caption">qui dit vraiment ce qu'il y a, et je relis</span> <span class="audio-time caption">encore, non seulement avant de committer, je</span> <span class="audio-time caption">relis toujours.</span> <span class="audio-time caption">Au minimum, je fais égal pour voir la différence.</span> <span class="audio-time caption">Ou s'il y a beaucoup de différences, je vais</span> <span class="audio-time caption">faire un grand <code>E</code> pour pouvoir en Ediff comparer</span> <span class="audio-time caption">les versions avant et les versions après,</span> <span class="audio-time caption">systématiquement.</span> <span class="audio-time caption">Mais en plus, je fais générer mon message de</span> <span class="audio-time caption">commit et je lis aussi le message de commit.</span> <span class="audio-time caption">Voilà, pour voir qu'il a bien compris et</span> <span class="audio-time caption">qu'effectivement aussi, le résumé est correct par</span> <span class="audio-time caption">rapport à ce que j'ai fait.</span> <span class="audio-time caption">Parce que parfois, s'il y a eu des centaines de</span> <span class="audio-time caption">changements, je pourrais voir aussi quelque chose</span> <span class="audio-time caption">qui me tient.</span> <span class="audio-time caption">Il me parle de ça, c'est bizarre qu'il me dise</span> <span class="audio-time caption">que... Je ne sais pas, que j'ai enlevé un</span> <span class="audio-time caption">paragraphe, ce n'est pas normal.</span> <span class="audio-time caption">Voilà, ça pourrait aussi m'attirer l'attention</span> <span class="audio-time caption">sur, tiens, est-ce que je n'ai quand même pas</span> <span class="audio-time caption">trop vite lu le diff et donc d'aller voir des</span> <span class="audio-time caption">trucs.</span> <span class="audio-time caption">Donc, c'est vraiment...</span> <span class="audio-time caption">Ça change la qualité du commit sous Git.</span> <span class="audio-time caption">C'est vraiment quelque chose que je conseille à</span> <span class="audio-time caption">tout le monde.</span> <span class="audio-time caption">Et donc ici, pour vc-dir, il a fallu que j'écrive</span> <span class="audio-time caption">ma fonction moi-même, parce que les autres Git</span> <span class="audio-time caption">gptel commits, il faut avoir stagé les fichiers,</span> <span class="audio-time caption">donc ça regarde ce qu'on a stagé, et ça fait un</span> <span class="audio-time caption">message de commit par rapport à ce qu'on a stagé.</span> <span class="audio-time caption">Maintenant, on essaie de dire, on ne stage pas.</span> <span class="audio-time caption">On ne voit pas cette opération-là, ça se fait</span> <span class="audio-time caption">tout seul.</span> <span class="audio-time caption">Donc on a les fichiers modifiés et puis on fait</span> <span class="audio-time caption">le commit directement.</span> <span class="audio-time caption">Donc ça fait le <code>git commit -a</code>, <code>git commit</code>
add…</span> <span class="audio-time caption">On ne passe pas par cette étape de staging.</span> <span class="audio-time caption">Et puis voilà, je trouve que c'est plus simple,</span> <span class="audio-time caption">simplement de sélectionner les fichiers, ceux que</span> <span class="audio-time caption">je veux, et puis de voir la différence, et puis</span> <span class="audio-time caption">de committer.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Oui, c'est mieux comme ça.</span> <p></p><div class="transcript-heading"><span class="audio-time">23:30</span> <strong>docstrings</strong></div><p></p><span class="audio-time caption"><strong>Sacha:</strong>  Tu utilises également gptel pour générer</span> <span class="audio-time caption">des docstrings.</span> <span class="audio-time caption">Peux-tu montrer ?</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Alors, on pourrait aller dans un fichier.</span> <span class="audio-time caption">Par exemple, ce fichier de gptel-commit-message.</span> <span class="audio-time caption">Donc voilà.</span> <span class="audio-time caption">Donc, j'ai une seule fonction.</span> <p></p><div class="transcript-heading"><span class="audio-time">24:03</span> <strong>hs-minor-mode - hideshow</strong></div><p></p><span class="audio-time caption"><strong>Fabrice:</strong> Et donc, chez moi, quand je rouvre par défaut,</span> <span class="audio-time caption">j'utilise aussi H.S. hs-minor-mode, hideshow.</span> <span class="audio-time caption">Donc, tous les corps des fonctions, etc., il les réduit.</span> <span class="audio-time caption">J'ai rajouté qui m'indique le nombre de lignes</span> <span class="audio-time caption">pour avoir quand même une petite idée, pas juste</span> <span class="audio-time caption">trois petits points, mais avoir une petite idée</span> <span class="audio-time caption">de ce qui se cache en dessous.</span> <span class="audio-time caption">Et donc après je fais metashifta chez moi, ce qui</span> <span class="audio-time caption">est visible mode.</span> <span class="audio-time caption">Donc ça m'ouvre.</span> <span class="audio-time caption">Il y a visible mode pour les fichiers Org avec</span> <span class="audio-time caption">les drawers et puis ça fait plusieurs choses</span> <span class="audio-time caption">visible mode et aussi HS show all.</span> <span class="audio-time caption">Donc ici, voilà, j'ai un message, j'ai une</span> <span class="audio-time caption">fonction, j'ai un message, un docstring et on va</span> <span class="audio-time caption">dire que j'ai écrit moi-même un docstring un peu</span> <span class="audio-time caption">plus bête.</span> <span class="audio-time caption">Je mets xxx à la place.</span> <span class="audio-time caption">Et donc, ce que je peux faire, c'est sélectionner</span> <span class="audio-time caption">toute la fonction.</span> <span class="audio-time caption">Oui, les métas, parenthèses, accolades, fréquentes.</span> <span class="audio-time caption">Ils ne sont pas toujours faciles sur un clavier</span> <span class="audio-time caption">français, d'ailleurs.</span> <span class="audio-time caption">Il faut faire le AltGr, tu vois.</span> <span class="audio-time caption">Le QWERTY est plus facile pour certains trucs.</span> <span class="audio-time caption">Donc ici, j'ai sélectionné, donc je vais faire</span> <span class="audio-time caption"><code>C-u C-c RET</code>.</span> <span class="audio-time caption">Donc je retombe sur le gptel menu.</span> <span class="audio-time caption">J'ai une directive traduite en français, donc ça,</span> <span class="audio-time caption">c'est pas très bon.</span> <span class="audio-time caption">Donc je vais changer la directive, donc je vais</span> <span class="audio-time caption">faire ici le <code>s</code> pour Set System Message.</span> <span class="audio-time caption">Encore une fois, le <code>s</code>, puisque on pourrait</span> <span class="audio-time caption">imaginer, si j'avais ça tout le temps, que j'ai</span> <span class="audio-time caption">write the string.</span> <span class="audio-time caption">Je n'ai pas de preset, de prompt avec ça, donc je</span> <span class="audio-time caption">vais essayer de le faire à la main.</span> <span class="audio-time caption">Je vais refaire S pour pouvoir moi-même éditer le message.</span> <span class="audio-time caption">Et donc ici, je vais taper à la place écrit un</span> <span class="audio-time caption">docstring correct montrant ce que la fonction fait.</span> <span class="audio-time caption"></span> <span class="audio-time caption">exactement.</span> <span class="audio-time caption"></span> <span class="audio-time caption">directive est correcte.</span> <span class="audio-time caption">Alors, je n’ai pas ?? le <code>b</code>, là, other</span> <span class="audio-time caption">buffer, ou je pourrais le mettre dans le kill ring aussi.</span> <span class="audio-time caption">On va le mettre dans le kill ring, par exemple,</span> <span class="audio-time caption">donc je vais faire <code>k</code>. Donc j’envoie la réponse</span> <span class="audio-time caption">dans le kill ring, <code>k</code>. Donc on lit bien, toujours en</span> <span class="audio-time caption">bas, ce qu'il va faire.</span> <span class="audio-time caption">Donc ici, quand je vais faire <code>RET</code>, il va envoyer</span> <span class="audio-time caption">les lignes sélectionnées avec la réponse dans le</span> <span class="audio-time caption">kill ring.</span> <span class="audio-time caption">Parfois, ce qui est dommage, c'est qu'on n'a pas</span> <span class="audio-time caption">un sablier comme des applications Windows.</span> <span class="audio-time caption">Voilà, ici, j'ai la réponse.</span> <span class="audio-time caption">C'est dommage qu'il n'y ait pas de sablier.</span> <span class="audio-time caption">Il y a tellement de choses qui passent sur</span> <span class="audio-time caption">l'echo area qu'on ne voit pas nécessairement ce qui se passe.</span> <span class="audio-time caption">Je vais remonter ici et on va imaginer, je vais</span> <span class="audio-time caption">directement effacer ça, faire C-y. Il a fait</span> <span class="audio-time caption">un peu trop.</span> <span class="audio-time caption">Alors, ben, voilà, ici en fait, c'est voilà,</span> <span class="audio-time caption">il faudrait je juste que j'édite un petit peu,</span> <span class="audio-time caption">voici un docstring correct. Il marque copié.</span> <span class="audio-time caption">Il faudrait voilà, il faudrait justement faire un prom pour lui dire</span> <span class="audio-time caption">ne me donne que le docstring sans répéter le nom de la fonction</span> <span class="audio-time caption">et cetera et cetera. Mais en gros, ouais, bah tout ça</span> <span class="audio-time caption">a l'air bon en fait hein.</span> <span class="audio-time caption">Ici, il m'avait rajouté du texte et des infos.</span> <span class="audio-time caption">Après, il explique ce qu'il a fait.</span> <span class="audio-time caption">Voilà.</span> <span class="audio-time caption">En gros, ça c'est bon.</span> <span class="audio-time caption">C'est juste que je ne sais pas pourquoi il a mal</span> <span class="audio-time caption">lié ceci.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  [Je me suis] trompée, peut-être.</span> <span class="audio-time caption">Tu as une fonction boost-gptel-generate-docstring</span> <span class="audio-time caption">dans ta configuration.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Ah ! Je ne l'ai pas encore testé, celle-là.</span> <span class="audio-time caption">Justement, avec la section de la fonction, je</span> <span class="audio-time caption">pense que ça ne marche pas toujours très bien.</span> <span class="audio-time caption">Il y a des choses qui ne marchent pas encore très bien.</span> <span class="audio-time caption">Il y a des choses qui sont en test un peu.</span> <span class="audio-time caption">Ça fait partie des tests.</span> <span class="audio-time caption">On peut réessayer.</span> <span class="audio-time caption">Je vais essayer de voir si elle marche.</span> <span class="audio-time caption">On voit ici qu'il a fait un docstring assez long,</span> <span class="audio-time caption">d'ailleurs.</span> <span class="audio-time caption">Un peu trop, à mon goût.</span> <span class="audio-time caption">En théorie, il doit détecter les bounds de la</span> <span class="audio-time caption">fonction et envoyer toute la fonction à gptel et</span> <span class="audio-time caption">avec un prompt.</span> <span class="audio-time caption">Ça a l'air de marcher assez bien.</span> <span class="audio-time caption">Sauf que de nouveau, il me fait plus, il me</span> <span class="audio-time caption">répète toute la fonction.</span> <span class="audio-time caption">Non, voilà, c'est ça, il me répète toute la fonction.</span> <span class="audio-time caption">Oui, jusque... Jusque l'interactif.</span> <span class="audio-time caption">Jusque là, oui, jusque l'interactif.</span> <span class="audio-time caption">Donc, c'est pas mal.</span> <span class="audio-time caption">Donc, oui, voilà, ça...</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Oui, pas mal.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Voilà, hop.</span> <span class="audio-time caption">On peut voir, effectivement... Donc, ça, c'est</span> <span class="audio-time caption">nouveau, c'est... Enfin, c'est... Changez,</span> <span class="audio-time caption">changez, oui.</span> <span class="audio-time caption">Et donc l'interactive, je n'avais pas</span> <span class="audio-time caption">d'interactive apparemment.</span> <span class="audio-time caption">Si je devais l'avoir.</span> <span class="audio-time caption">Je ne sais pas si j'ai un interactive ou pas,</span> <span class="audio-time caption">donc je fais <code>C-&lt;f9&gt;</code>.</span> <span class="audio-time caption">Et on peut voir ce qu'il a modifié.</span> <span class="audio-time caption">On va le faire en vertical.</span> <span class="audio-time caption">Hop.</span> <span class="audio-time caption">Oui, si j'avais un interactif avant.</span> <span class="audio-time caption">Il considère, il n'est pas aligné au niveau du truc.</span> <span class="audio-time caption">Forcément, je devais avoir un interactif.</span> <span class="audio-time caption">Pour terminer ou pas avec tout ce qui est</span> <span class="audio-time caption">intelligence artificielle, j'ai un petit truc ici</span> <span class="audio-time caption">qui traîne sur l'écran.</span> <span class="audio-time caption">Ça fait une semaine que je l'essaye.</span> <span class="audio-time caption">J'ai déjà payé pour la version si il y avait</span> <span class="audio-time caption">trois jours d'essai et ça me paraît très bien.</span> <p></p><div class="transcript-heading"><span class="audio-time">31:03</span> <strong>Super-whisper</strong></div><p></p><span class="audio-time caption"><strong>Fabrice:</strong> Donc c'est Super Whisper.</span> <span class="audio-time caption">Je vais le faire d'abord dans un Notepad par</span> <span class="audio-time caption">exemple pour vous montrer.</span> <span class="audio-time caption">Je l'ai mappé sur la touche Escape, ce qui n'est</span> <span class="audio-time caption">pas encore la meilleure touche.</span> <span class="audio-time caption">Je vais expliquer.</span> <span class="audio-time caption">Je voulais aussi pouvoir l'appeler à partir d'Emacs.</span> <span class="audio-time caption">J'aimerais bien que ce soit une touche assez</span> <span class="audio-time caption">facile, donc un peu une extrémité du clavier.</span> <span class="audio-time caption">La touche Escape, c'est une extrémité du clavier.</span> <span class="audio-time caption">Voilà, il n'y en a pas beaucoup, et puis il faut</span> <span class="audio-time caption">qu'elle ne soit pas utilisée dans Emacs, parce</span> <span class="audio-time caption">qu'il faut que je la fasse arriver dans Emacs.</span> <span class="audio-time caption">Et donc ici, quand je suis dans un endroit où on</span> <span class="audio-time caption">peut insérer du texte, donc ça pourrait être</span> <span class="audio-time caption">aussi en formulaire sur une page web, ici je suis</span> <span class="audio-time caption">dans Notepad, ça peut être dans Word, ça peut</span> <span class="audio-time caption">être n'importe où, je vais faire escape, et on</span> <span class="audio-time caption">voit à ce moment-là qu'il écoute.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  On voit des ondulations là.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Je ne le vois pas.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Je ne peux pas voir l'indication.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Parce que tu vas partager seulement le</span> <span class="audio-time caption">fenêtre Emacs.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Je vais stopper la présentation et</span> <span class="audio-time caption">je vais faire un share.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Je suis très curieuse à la reconnaissance vocale.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Voilà, screen 1.</span> <span class="audio-time caption">Voilà, ok.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Est-ce que vous le voyez ?</span> <span class="audio-time caption">Maintenant, oui.</span> <span class="audio-time caption">Oui, oui.</span> <span class="audio-time caption">Ok.</span> <span class="audio-time caption">On va bien danser dans le pad, ici.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Il y a une minute, j'ai fait Escape.</span> <span class="audio-time caption">Qui a lancé, donc, le Super Whisper.</span> <span class="audio-time caption">Et donc, on voit ici qu'il écoute, on voit des</span> <span class="audio-time caption">petites ondulations.</span> <span class="audio-time caption"></span> <span class="audio-time caption">Il y a une integration dans Notepad, aussi.</span> <span class="audio-time caption">Je le vois assez discret, d'ailleurs.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Je pense que parce que tu as aussi, dans</span> <span class="audio-time caption">ton séance de réunion virtuelle, la</span> <span class="audio-time caption">reconnaissance vocale a du mal avec l'audio.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Je pense que ça va aller, mais en</span> <span class="audio-time caption">fait, c'est ça qui est bien.</span> <span class="audio-time caption"></span> <span class="audio-time caption">donc W-I-S-P-R.</span> <span class="audio-time caption">En fait, ça marchait bien, sauf qu'il y avait des</span> <span class="audio-time caption">conflits avec Emacs.</span> <span class="audio-time caption">Et que dans Emacs, de temps en temps, j'avais</span> <span class="audio-time caption">des caractères qui étaient générés de manière aléatoire</span> <span class="audio-time caption">donc j'ai compris que c'était Wispr parce qu'en</span> <span class="audio-time caption">j'étais désactivée, quand j'aie désinstallé Wispr,</span> <span class="audio-time caption">je n'avais plus de problème avec Emacs</span> <span class="audio-time caption">de config. Et Wispr, lui, il écoutait ce que tu disais</span> <span class="audio-time caption">et tu voyais afficher au fur et à mesure les mots.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Peut-être tu peux désactiver Whisper,</span> <span class="audio-time caption">parce que on ne peut pas écouter bien</span> <span class="audio-time caption">ce que tu dit. Peut-être maintenant...</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Ok, excusez-moi, je n'avais pas</span> <span class="audio-time caption">compris, vous n'entendiez pas.</span> <span class="audio-time caption">Je n'avais pas compris, donc je</span> <span class="audio-time caption">vais réexpliquer.</span> <span class="audio-time caption">En fait, j'avais utilisé il y a quelques mois</span> <span class="audio-time caption">Wispr, W-I-S-P-R, et cette application-là, elle</span> <span class="audio-time caption">avait des conflits avec Emacs, donc j'avais des</span> <span class="audio-time caption">caractères qui généraient dans Emacs</span> <span class="audio-time caption">aléatoirement à un certain moment.</span> <span class="audio-time caption">Et quand je l'ai désinstallé, je n'ai plus eu ça,</span> <span class="audio-time caption">donc je ne sais pas pourquoi, mais il y avait des</span> <span class="audio-time caption">problèmes avec Emacs.</span> <span class="audio-time caption">Et l'application Wispr, elle écrivait les mots</span> <span class="audio-time caption">au fur et à mesure.</span> <span class="audio-time caption">SuperWhisper.com, je pense.</span> <span class="audio-time caption">Elle, elle écoute tout ce qu'on dit, donc je fais</span> <span class="audio-time caption">Escape pour l'activer, elle écoute tout ce que je</span> <span class="audio-time caption">dis, et puis quand je refais Escape, donc c'est</span> <span class="audio-time caption">un toggle, elle écrit tout d'un coup.</span> <span class="audio-time caption">Mais l'avantage, c'est qu'elle a du contexte.</span> <span class="audio-time caption">Et donc si je dis par exemple, je lui ai demandé</span> <span class="audio-time caption">comment ça va, en fait il va recopier, je lui ai</span> <span class="audio-time caption">demandé deux points, ouvrez les guillemets,</span> <span class="audio-time caption">comment ça va, point d'interrogation, fermez les guillemets.</span> <span class="audio-time caption">Parce qu'il interprète en fait, il reconstitue le</span> <span class="audio-time caption">paragraphe, le texte à la fin.</span> <span class="audio-time caption">Parfois, si je fais une faute et qu'il y a un</span> <span class="audio-time caption">autre orthographe, et puis je répète orthographe,</span> <span class="audio-time caption">il va comprendre que c'est le même mot.</span> <span class="audio-time caption">Il est assez malin, il remet le texte.</span> <span class="audio-time caption">de manière plus synthétique.</span> <span class="audio-time caption">Il met tous les mots, mais à la fin, au niveau</span> <span class="audio-time caption">typographie, etc., il met deux, trois petits points.</span> <span class="audio-time caption">Il met parfois des guillemets.</span> <span class="audio-time caption">Il comprend bien les questions.</span> <span class="audio-time caption">Je ne sais pas pourquoi ici, j'ai un caractère un</span> <span class="audio-time caption">peu spécial.</span> <span class="audio-time caption">Il y a quand même de temps en temps des fautes aussi.</span> <span class="audio-time caption">Tout à l'heure, je parlais de aléatoire.</span> <span class="audio-time caption">Il a marqué aléatoire.</span> <span class="audio-time caption">Oui, j'espère que j'ai cité tout à l'heure</span> <span class="audio-time caption">Donc voilà, il fallait marquer W, I, S, P, R. Et</span> <span class="audio-time caption">puis une fois, il a marqué c'est A, I, R. Bon, c'est</span> <span class="audio-time caption">pas parfait, mais en fait, ça marche et c'est</span> <span class="audio-time caption">beaucoup plus rapide quand même.</span> <span class="audio-time caption">Et j'écris une petite fonction qui marche dans</span> <span class="audio-time caption">WSL, en tout cas pour ma config, de telle façon</span> <span class="audio-time caption">que dans... Donc si je vais dans un buffer</span> <span class="audio-time caption">scratch, par exemple.</span> <span class="audio-time caption">Que dans Emacs, ça fait la même chose aussi quand</span> <span class="audio-time caption">je fais escape.</span> <span class="audio-time caption">Parfois, j'ai des problèmes de clipboard, donc le</span> <span class="audio-time caption">kill ring.</span> <span class="audio-time caption">Quand je fais le deuxième escape, parfois il me</span> <span class="audio-time caption">remet un vieux truc qui n'est pas ce que je viens</span> <span class="audio-time caption">de dire, donc il y a encore des choses à</span> <span class="audio-time caption">comprendre.</span> <span class="audio-time caption">Je vais essayer quand même juste une phrase,</span> <span class="audio-time caption">je fais <code>ESC</code>…</span> <span class="audio-time caption">Je sais pas... Ah, voilà. Il se lance. Mais...</span> <span class="audio-time caption">Il se lance. Est-ce que cela marche ?</span> <span class="audio-time caption">Ah, tu vois, j'ai un problème.</span> <span class="audio-time caption">J'ai dit est-ce que cela marche ?</span> <span class="audio-time caption">Il vient de remettre système, il faut que je comprenne.</span> <span class="audio-time caption">Il y a des petites configurations</span> <span class="audio-time caption">dans Super Whisper à faire pour le presse-papier.</span> <span class="audio-time caption">C'est un peu plus compliqué avec le presse-papier</span> <span class="audio-time caption">Windows et le presse-papier Emacs.</span> <span class="audio-time caption">J'ai encore des petits réglages à faire, mais</span> <span class="audio-time caption">quand ça marchera bien, ce sera fantastique de</span> <span class="audio-time caption">pouvoir directement aussi dans Emacs utiliser ça.</span> <span class="audio-time caption">Il y a peut-être d'autres solutions d'ailleurs.</span> <span class="audio-time caption">Ici, il marche assez bien et le fait qu'il insère</span> <span class="audio-time caption">le truc une fois qu'on a fini de parler, ça fait</span> <span class="audio-time caption">quand même des phrases qui sont plus correctes.</span> <span class="audio-time caption">On hésite parfois, donc ça reconstitue un truc de</span> <span class="audio-time caption">meilleure qualité.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  En fait, j'utilise aussi la</span> <span class="audio-time caption">reconnaissance vocale sur Emacs et j'utilise le</span> <span class="audio-time caption"><code>substitute</code> de Prot pour corriger facilement les</span> <span class="audio-time caption">erreurs et sauver les remplaçantes.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Et ça marche bien, donc ?</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Oui, oui, oui, ça marche.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Tu es contente ?</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Oui, je suis très contente.</span> <span class="audio-time caption">Je l'utilise pour les sous-titres, dicter mon texte.</span> <span class="audio-time caption">Ça marche bien.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Mais il faudrait que je regarde ta</span> <span class="audio-time caption">configuration, alors.</span> <span class="audio-time caption">Pour m'en inspirer, pour faire des tests aussi.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Si tu rediriges l'output de la</span> <span class="audio-time caption">reconnaissance vocale à Emacs, tu pourrais</span> <span class="audio-time caption">traiter avec Emacs Lisp pour faire des remplaçants.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  process sentinel, il y a les processus qui...</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Oui, j'ai lancé un serveur de [Speaches] pour offrir</span> <span class="audio-time caption">un service de la reconnaissance vocale qui</span> <span class="audio-time caption">utilise aussi Whisper, le mode Whisper.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Oui, il y a beaucoup de belles</span> <span class="audio-time caption">choses qui vont arriver, c'est sûr.</span> <span class="audio-time caption">En fait, le problème maintenant, c'est que je</span> <span class="audio-time caption">n'aime plus quand je suis ici au bureau parce que</span> <span class="audio-time caption">j'ai des collègues, on est en open space, donc je</span> <span class="audio-time caption">ne peux pas utiliser ça.</span> <span class="audio-time caption">C'est ça le problème.</span> <span class="audio-time caption">Quand je suis à la maison, je peux parler, mais</span> <span class="audio-time caption">quand on est au bureau, on ne peut pas.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Et dans ce temps, j'utilise avec les</span> <span class="audio-time caption">enregistrements que j'ai fait en marche, à pied,</span> <span class="audio-time caption">et d'autres environnements qui sont</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Et quand il y a les autres gens, il</span> <span class="audio-time caption">peut comprendre seulement toi ou il écrit toutes</span> <span class="audio-time caption">les phrases de les autres gens aussi?</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Je ne sais pas, en fait.</span> <span class="audio-time caption">Je ne l'utilise pas avec d'autres personnes.</span> <span class="audio-time caption">Et si je l'utilise, je vais utiliser le casque.</span> <span class="audio-time caption">Et je pense que le casque est assez... Le micro</span> <span class="audio-time caption">est assez directif.</span> <span class="audio-time caption">En gros, il n'y a quand même que moi qu'on</span> <span class="audio-time caption">entend, je pense.</span> <span class="audio-time caption">Il faudrait vraiment qu'il y ait des bruits assez</span> <span class="audio-time caption">forts pour que ça puisse passer aussi.</span> <p></p><div class="transcript-heading"><span class="audio-time">41:21</span> <strong>Dotfiler</strong></div><p></p><span class="audio-time caption"><strong>Sacha:</strong>  Je veux aussi consacrer du temps pour</span> <span class="audio-time caption">ton outil dotfiler pour gérer les fichiers</span> <span class="audio-time caption">professionnels et les fichiers personnels.</span> <span class="audio-time caption">Comment tu l'utilises-tu?</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Oui, alors ça, ce n'est pas de l'Emacs.</span> <span class="audio-time caption">Par contre, c'est très utile, y compris pour Emacs.</span> <span class="audio-time caption">Et donc, j'ai écrit ici une petite note là-dessus.</span> <span class="audio-time caption">Je vais ouvrir l'HTML, par exemple.</span> <span class="audio-time caption">Donc, là, ici, vous voyez bien l'HTML, oui, OK.</span> <span class="audio-time caption">Donc, en fait, j'utilise le programme dotfiler,</span> <span class="audio-time caption">donc c'est son nom, c'est le nom du programme.</span> <span class="audio-time caption">Il y a un outil qui s'appelait Stow dans le temps</span> <span class="audio-time caption">qui fait, je pense, à peu près la même chose.</span> <span class="audio-time caption">Je ne sais pas très bien les différences entre</span> <span class="audio-time caption">les deux, d'ailleurs.</span> <span class="audio-time caption">Mais donc l'idée en fait c'est, j'ai des</span> <span class="audio-time caption">repositories, donc j'ai par exemple, et ça me</span> <span class="audio-time caption">sert notamment pour Org, donc justement j'ai un</span> <span class="audio-time caption">repository avec des fichiers Org personnels, j'ai</span> <span class="audio-time caption">un repository avec des fichiers Org</span> <span class="audio-time caption">professionnels, et en fait, dotfiler, en</span> <span class="audio-time caption">fait c'est plus clair ici dans mon explication,</span> <span class="audio-time caption">donc j'ai un repo A,</span> <span class="audio-time caption">Et sous le repo A, j'ai un répertoire Org, j'ai</span> <span class="audio-time caption">un répertoire bin, j'ai un répertoire Lisp.</span> <span class="audio-time caption">Repo B, je ne suis pas obligé d'avoir les trois.</span> <span class="audio-time caption">Donc, j'ai une autre structure, mais j'ai par</span> <span class="audio-time caption">exemple Org, bin, Lisp aussi, examples, peu</span> <span class="audio-time caption">importe.</span> <span class="audio-time caption">Et donc, quand on utilise dotfiler,</span> <span class="audio-time caption">Il va créer, donc tout ça est mis par défaut dans</span> <span class="audio-time caption">.dotfiles sous le home directory.</span> <span class="audio-time caption">C'est là que tous les repos se trouvent, ceux qui</span> <span class="audio-time caption">sont gérés par dotfiler.</span> <span class="audio-time caption">Et donc j'ai fait des clones, donc au début je</span> <span class="audio-time caption">vais faire, donc ici si je vais dans mes</span> <span class="audio-time caption">.dotfiles, c'est tous les repos que j'ai.</span> <span class="audio-time caption">Il y a des repos privés et des repos publics.</span> <span class="audio-time caption">Et je vais faire un dot add d'une URL Git.</span> <span class="audio-time caption">Par exemple, si je veux cloner un nouveau</span> <span class="audio-time caption">repository.</span> <span class="audio-time caption">Et donc, il va mettre sous .dotfiles, il va</span> <span class="audio-time caption">mettre repo c, par exemple.</span> <span class="audio-time caption">Donc, si j'ai fait un dot add.</span> <span class="audio-time caption">Donc, la commande, c'est dot update, dot add.</span> <span class="audio-time caption">Donc je vais faire un add d'un repo C. Donc le</span> <span class="audio-time caption">repo va être cloné localement sous dot files, un</span> <span class="audio-time caption">repo C. Et puis je vais faire un dot update.</span> <span class="audio-time caption">Donc quand l'update, imaginons au départ que</span> <span class="audio-time caption">j'avais que le repo A, et puis je fais ça, donc</span> <span class="audio-time caption">j'ajoute un repo B. Comment on va faire ?</span> <span class="audio-time caption">Donc au départ, quand je n'ai que le repo A, ce</span> <span class="audio-time caption">qu'il fait, c'est que tout ce qu'il trouve en</span> <span class="audio-time caption">dessous de chaque repo,</span> <span class="audio-time caption">Il les met directement via des symlinks sous le</span> <span class="audio-time caption">home directory.</span> <span class="audio-time caption">Donc le répertoire Org va se retrouver via un</span> <span class="audio-time caption">symlink sous le home directory.</span> <span class="audio-time caption">Le répertoire bin va se retrouver sous le home</span> <span class="audio-time caption">directory.</span> <span class="audio-time caption">Et pareil pour l'isp.</span> <span class="audio-time caption">Donc si je n'ai qu'un repo au début d'ailleurs et</span> <span class="audio-time caption">que je fais un update, dot update, donc il va</span> <span class="audio-time caption">créer des symlinks.</span> <span class="audio-time caption">Un dot update crée ou détruit des symlinks.</span> <span class="audio-time caption">Ça fait juste ça, en fonction de ce qui a évolué.</span> <span class="audio-time caption">Et donc la première fois, il va juste faire un</span> <span class="audio-time caption">symlink du répertoire tilde slash Org vers</span> <span class="audio-time caption">tilde.dotfiles repo a Org.</span> <span class="audio-time caption">Et il va faire la même chose avec bin, la même</span> <span class="audio-time caption">chose avec Lisp.</span> <span class="audio-time caption">Quand j'ai le deuxième repo, le repo b, que j'ai</span> <span class="audio-time caption">colonné, j'ai fait...</span> <span class="audio-time caption">dot add de ce repo-là.</span> <span class="audio-time caption">Puis je fais un update.</span> <span class="audio-time caption">À ce moment-là, évidemment, il y a deux</span> <span class="audio-time caption">répertoires Org qui doivent être symlinkés.</span> <span class="audio-time caption">Ils doivent avoir des liens symboliques dans le</span> <span class="audio-time caption">tilde.</span> <span class="audio-time caption">Donc, il ne peut plus faire ça au niveau du</span> <span class="audio-time caption">répertoire.</span> <span class="audio-time caption">Et donc, ce qu'il fait, c'est qu'il supprime le</span> <span class="audio-time caption">symlink qu'il avait sur le répertoire Org et il</span> <span class="audio-time caption">scanne tous les fichiers qui sont là-dedans et il</span> <span class="audio-time caption">va faire un symlink individuel, fichier par</span> <span class="audio-time caption">fichier.</span> <span class="audio-time caption">Donc si j'ai 10 fichiers ici, donc fichier 1,</span> <span class="audio-time caption">fichier 2, fichier 3, il va me faire des symlinks</span> <span class="audio-time caption">dans tilde Org fichier 1 vers le fichier 1 qui se</span> <span class="audio-time caption">trouve physiquement là.</span> <span class="audio-time caption">Et ainsi de suite pour tous les fichiers.</span> <span class="audio-time caption">Il va faire la même chose avec tous les fichiers</span> <span class="audio-time caption">qu'il va trouver ici.</span> <span class="audio-time caption">Et donc tous les fichiers qui se trouvaient dans</span> <span class="audio-time caption">repo A Org et dans repo B Org, via des symlinks,</span> <span class="audio-time caption">se retrouvent en fait dans tilde Org.</span> <span class="audio-time caption">Et pareil ici dans mon exemple avec bin et avec</span> <span class="audio-time caption">Lisp.</span> <span class="audio-time caption">Donc si je vais voir ici, si je vais dans mon</span> <span class="audio-time caption">répertoire bin, vous voyez, donc j'ai, je vais</span> <span class="audio-time caption">faire un peu plus petit, j'en ai des, enfin on ne</span> <span class="audio-time caption">va pas, ici, voilà, celui-ci, il vient de</span> <span class="audio-time caption">Archibus Reports, de ce repo-là.</span> <span class="audio-time caption">Celui-ci, il vient de Archibus, OIL, MCD,</span> <span class="audio-time caption">Datamodel, Diagram.</span> <span class="audio-time caption">Donc de ce repo-là.</span> <span class="audio-time caption">Ici, il vient de gitboost.</span> <span class="audio-time caption">Donc j'avais dans ces différents répertoires,</span> <span class="audio-time caption">j'avais un sous-répertoire bin.</span> <span class="audio-time caption">Donc dans ces différents repos, j'avais un</span> <span class="audio-time caption">sous-répertoire bin.</span> <span class="audio-time caption">Et tous ces fichiers qui se trouvent directement</span> <span class="audio-time caption">dans bin, sous les repos,</span> <span class="audio-time caption">se retrouvent in fine via des symlinks dans mon</span> <span class="audio-time caption">bin à moi, dans mon directory.</span> <span class="audio-time caption">Ce qui veut dire que dans ma configuration de mon</span> <span class="audio-time caption">shell, j'ai juste à dire path égale tilde slash</span> <span class="audio-time caption">bin de point $path.</span> <span class="audio-time caption">Et donc il va connaître tous mes petits scripts</span> <span class="audio-time caption">qui sont dans les bins d'un coup.</span> <span class="audio-time caption">Pour Org,</span> <span class="audio-time caption">Ça veut dire que, org-agenda-files, je fais</span> <span class="audio-time caption">pointer simplement vers tout ce qui se trouve</span> <span class="audio-time caption">dans <code>~/org</code>.</span> <span class="audio-time caption">Et vu que dans <code>~/org</code>, je retrouve tous les</span> <span class="audio-time caption">fichiers Org du repo A, du repo B, et ainsi de</span> <span class="audio-time caption">suite, j’ai une seule variable, un seul <code>setq</code>, et</span> <span class="audio-time caption">j'ai tous mes fichiers qui se retrouvent</span> <span class="audio-time caption">directement dedans.</span> <span class="audio-time caption">Et donc, quelle que soit la machine, si j'ai</span> <span class="audio-time caption">différentes machines, une machine pro, une</span> <span class="audio-time caption">machine perso.</span> <span class="audio-time caption">Sur ma machine perso, j'aurai mon repo privé et</span> <span class="audio-time caption">mon repo pro.</span> <span class="audio-time caption">Sur la machine professionnelle, j'aurai que le</span> <span class="audio-time caption">repo professionnel.</span> <span class="audio-time caption">En fait, mon fichier Emacs ne change pas.</span> <span class="audio-time caption">C'est le même fichier.</span> <span class="audio-time caption">Je dis juste scan tous les fichiers.</span> <span class="audio-time caption">Mon Emacs, en se lançant, il fait toujours un</span> <span class="audio-time caption">scan de tout ce qu'il y a. Donc, il va,</span> <span class="audio-time caption"><code>org-agenda-files</code> va regarder tout ce qu’il y a dans</span> <span class="audio-time caption"><code>~/org</code> et tout ça est rajouté à mon
<code>org-agenda-files</code>.</span> <span class="audio-time caption">Et donc, il va, à chaque lancement d'Emacs, il va</span> <span class="audio-time caption">voir tous les fichiers qui sont dans la</span> <span class="audio-time caption">configuration.</span> <span class="audio-time caption">Mais c'est le même fichier Emacs.</span> <span class="audio-time caption">Pour ça, il ne change pas.</span> <span class="audio-time caption">C'est le même fichier Emacs d'un côté ou de</span> <span class="audio-time caption">l'autre.</span> <span class="audio-time caption">Simplement d'aller voir tous les fichiers qui</span> <span class="audio-time caption">sont en <code>~/org</code>.</span> <span class="audio-time caption">Et en fait, c'est des symlinks vers les fichiers</span> <span class="audio-time caption">qui sont dans un repo privé, un repo public, etc.</span> <span class="audio-time caption">Pareil avec le bin.</span> <span class="audio-time caption">Donc ça vient de 36 repositories.</span> <span class="audio-time caption">Pareil avec le Lisp.</span> <span class="audio-time caption">Justement, et ça me force un peu aussi à faire</span> <span class="audio-time caption">une structure un peu plus standard.</span> <span class="audio-time caption">Donc ici, si je vais dans… dans <code>~/.dotfiles</code>…</span> <span class="audio-time caption">gptel-commit-message.</span> <span class="audio-time caption">J'ai un répertoire.</span> <span class="audio-time caption">J'ai un répertoire Lisp.</span> <span class="audio-time caption">Et c'est dedans, donc c'est des lisps.</span> <span class="audio-time caption">J'ai ici le petit package, le petit fichier avec</span> <span class="audio-time caption">la fonction pour écrire le message de commit.</span> <span class="audio-time caption">Si je vais dans... Si je remonte...</span> <span class="audio-time caption">Je vais dans Emacs Leuven Lisp.</span> <span class="audio-time caption">Là, j'ai tous mes fichiers de configuration.</span> <span class="audio-time caption">Et tout ça se retrouve en fait dans <code>~/lisp</code>.</span> <span class="audio-time caption">Grâce à symlink.</span> <span class="audio-time caption">Donc si je vais ici dans <code>~/lisp</code>.</span> <span class="audio-time caption">Lisp.</span> <span class="audio-time caption">Voilà.</span> <span class="audio-time caption">Dotfiler, Emacs gptel, mon fichier de</span> <span class="audio-time caption">configuration, le nouveau avec gptel.</span> <span class="audio-time caption">Qu’il voit dans <code>~/lisp</code>, en fait, physiquement,</span> <span class="audio-time caption">il est dans <code>/home/fni/.dotfiles/emacs-leuven/lisp</code>,</span> <span class="audio-time caption">il est physiquement là.</span> <span class="audio-time caption">Celui avec le gptel-commit-message,</span> <span class="audio-time caption">Physiquement, il est dans un autre repo, il est</span> <span class="audio-time caption">dans le repo gptel-commit-message, sous le</span> <span class="audio-time caption">répertoire Lisp.</span> <span class="audio-time caption">Donc en fait, en dessous de chaque repository,</span> <span class="audio-time caption">tous les fichiers qui sont là vont directement</span> <span class="audio-time caption">tel quel dans le home et tous les répertoires qui</span> <span class="audio-time caption">sont là vont directement tel quel dans le home.</span> <span class="audio-time caption">Donc en fait, il faut que je vois vraiment tous</span> <span class="audio-time caption">mes repos comme des puzzles, pièces de puzzle.</span> <span class="audio-time caption">Et tous les fichiers ou tous les répertoires à la</span> <span class="audio-time caption">racine de chaque repo vont se retrouver via des</span> <span class="audio-time caption">symlinks</span> <span class="audio-time caption">dans mon <code>~</code></span> <span class="audio-time caption">Directement en tant que fichier.</span> <span class="audio-time caption">Par exemple, j’ai toujours un <code>README.org</code> dans</span> <span class="audio-time caption">chacun de mes repos.</span> <span class="audio-time caption">Là, c'est une exception parce que on peut dire</span> <span class="audio-time caption">celui-là n’essaie pas parce que j’ai <code>README.org</code></span> <span class="audio-time caption">dans chacun de mes repos.</span> <span class="audio-time caption">Donc, il ne saurait pas créer des symlinks d'un</span> <span class="audio-time caption"><code>README.org</code> dans <code>~</code> vers tous les
repositories.</span> <span class="audio-time caption">Celui-là, c'est une exception.</span> <span class="audio-time caption">On me dit de ne pas le symlinker.</span> <span class="audio-time caption">Et tous les autres fichiers se retrouvent</span> <span class="audio-time caption">directement dans mon <code>~</code> avec des symlinks.</span> <span class="audio-time caption">Et tous les répertoires, pareil.</span> <span class="audio-time caption">S'il y a des répertoires qui sont communs, donc</span> <span class="audio-time caption">j'ai chaque fois plusieurs fois des répertoires</span> <span class="audio-time caption">bin dans les repositories, donc à ce moment-là,</span> <span class="audio-time caption">il fait des symlinks sur les fichiers qui sont</span> <span class="audio-time caption">dans les répertoires bin.</span> <span class="audio-time caption">Et donc pour le Lisp, pareil, dans Emacs, j'ai</span> <span class="audio-time caption">juste dit de scanner tous les fichiers que j'ai</span> <span class="audio-time caption">dans ~ Lisp et de faire un load library.</span> <span class="audio-time caption">Donc je fais un dolist avec un find, etc.,</span> <span class="audio-time caption">Et donc en une seule commande, qui reste la même</span> <span class="audio-time caption">sur la machine professionnelle et la machine</span> <span class="audio-time caption">privée, j'ai la même commande qui fait scanner</span> <span class="audio-time caption">tout contenu mon <code>~/lisp</code> et donc dynamiquement,</span> <span class="audio-time caption">sur une machine, j'aurai potentiellement plus de</span> <span class="audio-time caption">fichiers Lisp ou pas, mais c'est la même commande</span> <span class="audio-time caption">et je n'ai pas de config particulier à faire.</span> <span class="audio-time caption">C'est juste en fonction des repositories que je</span> <span class="audio-time caption">vais cloner sur telle ou telle machine que</span> <span class="audio-time caption">j'aurai plus ou moins de fichiers disponibles et</span> <span class="audio-time caption">tout ça est caché.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Il y a une question dans le chat.</span> <span class="audio-time caption">Ces liens sont gérés dans Git ?</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Non, non.</span> <span class="audio-time caption">Imaginons... Je peux le montrer en live si tu veux.</span> <span class="audio-time caption">Je vais aller dans Emacs... Donc, cd.</span> <span class="audio-time caption">Donc dans .dotfiles, dans emacs-chat-sacha-chua.</span> <span class="audio-time caption">Donc dans ce répertoire-là, j'ai une image, j'ai</span> <span class="audio-time caption">quelques fichiers, d'accord ?</span> <span class="audio-time caption">Et je vais rajouter un fichier.</span> <span class="audio-time caption">Fichier des mots, .Org.</span> <span class="audio-time caption">Voilà, donc ici, maintenant, j'ai un fichier de</span> <span class="audio-time caption">plus dans ce repository.</span> <span class="audio-time caption">Je pourrais l'ajouter dans Git, etc.</span> <span class="audio-time caption">Mais je n'ai même pas besoin de faire ça, en fait.</span> <span class="audio-time caption">Pour les symlinks, je peux... Donc, pour</span> <span class="audio-time caption">l'instant, ce fichier Org, imaginons que j'ai des</span> <span class="audio-time caption">tâches dedans.</span> <span class="audio-time caption">Pour l'instant, il sera... Dans ce cas-là,</span> <span class="audio-time caption">d'ailleurs, je devrais... Enfin, je vais... Je</span> <span class="audio-time caption">vais créer un répertoire Org et je vais le</span> <span class="audio-time caption">déplacer, fichier des mots, temps Org.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  Voilà.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Donc, ici, dans...</span> <span class="audio-time caption">Dans ce répertoire-là, j'ai ce fichier de démo et</span> <span class="audio-time caption">on peut imaginer que j'ai des tâches.</span> <span class="audio-time caption">J'aimerais bien voir ce fichier-là dans mon Org</span> <span class="audio-time caption">agenda files.</span> <span class="audio-time caption">Et pour l'instant, il n'est pas visible à partir</span> <span class="audio-time caption">de mon tilde.</span> <span class="audio-time caption">Il est physiquement là, mais il n'y a pas de</span> <span class="audio-time caption">symlink dessus.</span> <span class="audio-time caption">Pour que le symlink soit créé, je vais juste</span> <span class="audio-time caption">faire .dot files bin.</span> <span class="audio-time caption">Dans bin, il y a la commande dot.</span> <span class="audio-time caption">On peut voir son aide.</span> <span class="audio-time caption">Il y a dot update, dot status, dot add.</span> <span class="audio-time caption">Add, c'est pour ajouter un nouveau repository.</span> <span class="audio-time caption">Et update, c'est pour scanner le contenu des</span> <span class="audio-time caption">repositories et mettre à jour les liens.</span> <span class="audio-time caption">Ici, je vais faire dot update.</span> <span class="audio-time caption">Et alors, je vais faire moins skip pool parce que</span> <span class="audio-time caption">sinon, si on ne fait pas moins skip pool, il va</span> <span class="audio-time caption">en même temps pooler les repos.</span> <span class="audio-time caption">Donc, on va essayer que tout soit à jour.</span> <span class="audio-time caption">Donc ici, je vais faire le <code>--skip-pull</code>.</span> <span class="audio-time caption">Et on va voir qu'il va créer un lien, en fait,</span> <span class="audio-time caption">Vers ce fichier-là, voilà.</span> <span class="audio-time caption">Donc ici, il a créé, donc dans mon Org, donc dans</span> <span class="audio-time caption">mon tilde en fait, ça c'est mon tilde, donc dans</span> <span class="audio-time caption"><code>~/org</code>, fichier-demo.Org, il l’a lié vers la</span> <span class="audio-time caption">position physique du chemin.</span> <span class="audio-time caption">Donc ça veut dire que maintenant, je peux dans</span> <span class="audio-time caption">Emacs, ici, donc si je fais <code>C-x C-f</code>, dans Org,</span> <span class="audio-time caption">donc c'est des fichiers, voilà.</span> <span class="audio-time caption">Il est directement là.</span> <span class="audio-time caption">Donc il est vide.</span> <span class="audio-time caption">Voilà, et donc ici, fichiers des mots, ça pointe</span> <span class="audio-time caption">dans mon type d'org, il pointe vers sa position</span> <span class="audio-time caption">physique.</span> <span class="audio-time caption">Donc les symlinks, c'est lui qui gère ça, c'est</span> <span class="audio-time caption">lui qui met à jour en fonction des fichiers</span> <span class="audio-time caption">ajoutés ou des fichiers supprimés.</span> <span class="audio-time caption">Il garde, il compare, voilà.</span> <span class="audio-time caption">Il y a tout ça dans les repos.</span> <span class="audio-time caption">Il y a tous ces symlinks qui existent à partir de</span> <span class="audio-time caption">~ et il met à jour.</span> <span class="audio-time caption">Il rajoute ou il retire en fonction.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  C'est Git qui garde toujours le</span> <span class="audio-time caption">fichier original.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Et donc dans Git, tu as les</span> <span class="audio-time caption">fichiers physiques.</span> <span class="audio-time caption">Git ne voit pas de symlinks, etc.</span> <span class="audio-time caption">Dans Git, tu as les fichiers physiques.</span> <span class="audio-time caption">Ici, si je vais... Imaginons que je mette Hello.</span> <span class="audio-time caption">Dans Git, je vais faire vc-dir, <code>C-&lt;f9&gt;</code>.</span> <span class="audio-time caption"><code>C-&lt;f9&gt;</code>.</span> <span class="audio-time caption">Qu'est-ce qui se passe ?</span> <span class="audio-time caption">Peut-être parce qu'il n'est pas... Ah oui, c'est</span> <span class="audio-time caption">parce que justement, je suis sur le Simlink, là,</span> <span class="audio-time caption">il faut que j'aille dans dotfiles.</span> <span class="audio-time caption">Donc c'était Emacs, chat, Sacha, voilà.</span> <span class="audio-time caption">Donc là, je fais <code>C-&lt;f9&gt;</code>.</span> <span class="audio-time caption">Et donc je vois que j'ai un nouveau fichier à ajouter.</span> <span class="audio-time caption">Et là, ce n'est pas un Simlink, ce sera le</span> <span class="audio-time caption">fichier physique.</span> <span class="audio-time caption">Donc au niveau Git, c'est les fichiers physiques</span> <span class="audio-time caption">dans des repos.</span> <span class="audio-time caption">Simplement, conventionnellement, tous mes repos</span> <span class="audio-time caption">vont se retrouver en dessous de <code>~/.dotfiles</code>.</span> <span class="audio-time caption">C'est là qu'ils scannent tout ce qu'il y a en</span> <span class="audio-time caption">dessous de <code>~/.dotfiles</code>.</span> <span class="audio-time caption">C'est là qu'ils scannent tous les repositories et</span> <span class="audio-time caption">qu'ils regardent le contenu de chaque repository.</span> <span class="audio-time caption">Si j'avais ton site web ou ta configuration, tu</span> <span class="audio-time caption">rajoutes un fichier, je vais faire <code>dot update</code>.</span> <span class="audio-time caption">Il va les puller</span> <span class="audio-time caption">Et puis, si tu as rajouté des fichiers, si tu as</span> <span class="audio-time caption">fait des modifications, il ne se passe rien.</span> <span class="audio-time caption">Au niveau des symlinks, il ne se passe rien.</span> <span class="audio-time caption">Puisque simplement, j'aurai la nouvelle version à</span> <span class="audio-time caption">jour dans mon clone, dans le sandbox.</span> <span class="audio-time caption">Mais s'il y a des nouveaux fichiers ou des</span> <span class="audio-time caption">fichiers qui ont été enlevés, il va rajouter ou</span> <span class="audio-time caption">effacer des symlinks.</span> <span class="audio-time caption">Maintenant, si j'efface ce fichier-là, donc un</span> <span class="audio-time caption">delete, je vais l’effacer de <code>.dotfiles</code>. Emacs.</span> <span class="audio-time caption">Sacha Chua, le chat.</span> <span class="audio-time caption">Donc je vais effacer le répertoire ou le fichier ici.</span> <span class="audio-time caption">Donc hash, yes.</span> <span class="audio-time caption">Voilà, le fichier n'existe plus dans mon repository.</span> <span class="audio-time caption">Donc je vais relancer dot update avec le
<code>--skip-pull</code>.</span> <span class="audio-time caption">Il va montrer rm, voilà.</span> <span class="audio-time caption">Donc il vient d'effacer le symlink vers tilt Org.</span> <span class="audio-time caption">C'est génial parce qu'en fait, dans le temps,</span> <span class="audio-time caption">chaque fois que j'ai rajouté un repository, il</span> <span class="audio-time caption">fallait voir où se trouvent les bins, puis faire</span> <span class="audio-time caption">add dans le path de tous les bins.</span> <span class="audio-time caption">Ici, non, tout se retrouve, pour autant que ce</span> <span class="audio-time caption">soit directement dans le repository</span> <span class="audio-time caption">sous <code>./bin</code>, ça va se retrouver après le symlink dans</span> <span class="audio-time caption"><code>~/bin</code>.</span> <span class="audio-time caption">Et donc j'ai qu'à rajouter une fois le fait que</span> <span class="audio-time caption">Tiltbin est dans mon path.</span> <span class="audio-time caption">Donc ça pourrait marcher pour les manpaths, pour</span> <span class="audio-time caption">les fichiers d'info, enfin pour les lists, pour</span> <span class="audio-time caption">l'org, donc c'est extrêmement pratique pour ça.</span> <p></p><div class="transcript-heading"><span class="audio-time">59:12</span> <strong>Enseignement d'Emacs</strong></div><p></p><span class="audio-time caption"><strong>Sacha:</strong>  Pendant les dernières minutes,</span> <span class="audio-time caption">j'aimerais connaître ta point de vue sur</span> <span class="audio-time caption">l'enseignement d'Emacs.</span> <span class="audio-time caption">Comment tu structures ton cours pour ne pas noyer</span> <span class="audio-time caption">les gens sur les fonctionnalités?</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Mon cours, je le vois plus comme</span> <span class="audio-time caption">étant un cours pour des gens qui connaissent déjà</span> <span class="audio-time caption">Emacs, donc c'est plutôt pour leur montrer des</span> <span class="audio-time caption">choses qu'ils ne connaissent peut-être pas parce</span> <span class="audio-time caption">qu'ils n'ont pas lu des dizaines ou des centaines</span> <span class="audio-time caption">ou des milliers de fichiers de configuration</span> <span class="audio-time caption">comme moi.</span> <span class="audio-time caption">Donc voilà, c'est des gens qui a priori</span> <span class="audio-time caption">connaissent déjà Emacs, donc ils savent faire</span> <span class="audio-time caption">enregistrer, ouvrir un fichier,</span> <span class="audio-time caption">Et simplement, je vais leur montrer des choses en</span> <span class="audio-time caption">plus, comme Helm.</span> <span class="audio-time caption">Moi, j'utilise Helm pour tout ce qui est la</span> <span class="audio-time caption">gestion des fichiers et des buffers.</span> <span class="audio-time caption">Je vais leur montrer aussi des fonctionnalités</span> <span class="audio-time caption">parfois qui sont peu utilisées ou parfois un peu</span> <span class="audio-time caption">cachées, donc peu utilisées comme les macros ou</span> <span class="audio-time caption">le multiple curseur qu'on ne connaît pas</span> <span class="audio-time caption">nécessairement et qui n'est pas standard, mais</span> <span class="audio-time caption">qui est facile à installer.</span> <span class="audio-time caption">ou l'édition rectangle, qui est standard, mais</span> <span class="audio-time caption">que pendant longtemps, je n'avais pas connu.</span> <span class="audio-time caption">Et donc voilà, je vais leur montrer des choses</span> <span class="audio-time caption">comme ça.</span> <span class="audio-time caption">A priori, c'est des gens qui connaissent déjà,</span> <span class="audio-time caption">donc ça va un peu plus vite.</span> <span class="audio-time caption">Maintenant, j'avais quand même une fois dans un</span> <span class="audio-time caption">de mes cours, Martin, qui ne me connaissait pas</span> <span class="audio-time caption">du tout Emacs.</span> <span class="audio-time caption">Son père connaissait Emacs, il l'avait amené de</span> <span class="audio-time caption">force presque.</span> <span class="audio-time caption">Si on écoute et qu'on suit, je repassais aussi.</span> <span class="audio-time caption">En fait, je réexpliquais tout depuis le début.</span> <span class="audio-time caption">J’avais des slides sur <code>C-a</code>, <code>C-e</code>,
<code>M-n</code>,</span> <span class="audio-time caption"><code>M-p</code>, <code>C-s</code>, <code>C-r</code>.</span> <span class="audio-time caption">Je réexpliquais tout plus ou moins rapidement en</span> <span class="audio-time caption">fonction du niveau des gens, s'ils connaissent déjà.</span> <span class="audio-time caption">Je vais aller assez vite.</span> <span class="audio-time caption">En fait, c'est plus facile si les gens</span> <span class="audio-time caption">connaissent déjà pour aller plus vite.</span> <span class="audio-time caption">Sinon, il faudrait plus que deux jours de formation.</span> <span class="audio-time caption">En fait, j'ai quand même fait quatre jours de</span> <span class="audio-time caption">formation Emacs.</span> <span class="audio-time caption">Donc, c'est deux jours sur ce que j'appelle les</span> <span class="audio-time caption">fondamentaux.</span> <span class="audio-time caption">Tout ce qui est édition rectangle, macro,</span> <span class="audio-time caption">multiple curseur, donc avec des choses en plus.</span> <span class="audio-time caption">Et puis, deux jours plus orientés sur Org.</span> <span class="audio-time caption">Il y a tellement de choses dans Org.</span> <span class="audio-time caption">Il y a les Org agendas, le time blocking que</span> <span class="audio-time caption">j'utilise pour tout, pour mon temps, pour faire</span> <span class="audio-time caption">des factures derrière.</span> <span class="audio-time caption">Et puis il y a évidemment Babel, Tangling.</span> <span class="audio-time caption">Donc tout mon fichier de configuration Emacs, il</span> <span class="audio-time caption">est dans un fichier qui est tanglé.</span> <span class="audio-time caption">Un fichier de doc, la code littérée de</span> <span class="audio-time caption">programming de Knuth, où je décris pour un humain.</span> <span class="audio-time caption">Et puis j'ai des petits bouts de code qui vont se retrouver.</span> <span class="audio-time caption">dans des fichiers de code où il faut pour être exécuté.</span> <span class="audio-time caption">Mais l'objectif, c'est de faire un document qui</span> <span class="audio-time caption">soit lisible par un humain d'abord.</span> <span class="audio-time caption">Et donc, même en quatre jours, ça passe vite.</span> <span class="audio-time caption">Il y a trop à montrer.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  C'est aussi le point lié à des</span> <span class="audio-time caption">incertitudes économiques.</span> <span class="audio-time caption">Quels sont les avantages du coaching ou de</span> <span class="audio-time caption">l'enseignement humain par rapport à l'IA?</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Donc, répète les avantages du</span> <span class="audio-time caption">coaching par rapport à l'IA.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Parce qu'il est facile de poser des</span> <span class="audio-time caption">questions à l'IA gratuitement, mais je pense que</span> <span class="audio-time caption">tes formations, ton cours ont des avantages.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Absolument.</span> <span class="audio-time caption">En fait, dans tous les domaines, c'est comme ça.</span> <span class="audio-time caption">C'est que l'IA, si j'ai une question précise ou</span> <span class="audio-time caption">une question claire, il va pouvoir me répondre</span> <span class="audio-time caption">très facilement et de manière avec plein de</span> <span class="audio-time caption">détails, donc extrêmement fantastique.</span> <span class="audio-time caption">Mais l'IA, le problème, c'est qu'on ne sait pas</span> <span class="audio-time caption">ce qu'on ne sait pas.</span> <span class="audio-time caption">Et donc, dans la formation, je me doute que plein</span> <span class="audio-time caption">de gens, même qui sont des utilisateurs d'Emacs</span> <span class="audio-time caption">depuis 20 ans,</span> <span class="audio-time caption">Plein de gens ne connaissent pas l'édition</span> <span class="audio-time caption">rectangle, utilisent peu ou pas les macros, ne</span> <span class="audio-time caption">connaissent pas Wdired, le Dired mode éditable, par exemple.</span> <span class="audio-time caption">Si je mets des photos sur mon disque, je veux</span> <span class="audio-time caption">mettre la date de la photo dans le nom du</span> <span class="audio-time caption">fichier, je vais passer en mode direct éditable</span> <span class="audio-time caption">et je vais faire une petite macro qui va me</span> <span class="audio-time caption">copier la date.</span> <span class="audio-time caption">Et donc voilà, en fait, ChatGPT, si je dis,</span> <span class="audio-time caption">ChatGPT, explique-moi comment marche l'édition</span> <span class="audio-time caption">rectangle, il va l'expliquer.</span> <span class="audio-time caption">En fait, il ne va jamais répondre à dire, tiens,</span> <span class="audio-time caption">il y a l'édition rectangle que tu ne connais pas.</span> <span class="audio-time caption">C'est ça le problème.</span> <span class="audio-time caption">C'est justement de pouvoir juger ce que les gens</span> <span class="audio-time caption">savent et de ce qu'on pourrait leur apporter en</span> <span class="audio-time caption">plus.</span> <span class="audio-time caption">Et là, l'IA va être moins bonne parce qu'elle n'a</span> <span class="audio-time caption">pas des propositions comme ça de choses que tu ne</span> <span class="audio-time caption">connais pas.</span> <span class="audio-time caption">C'est de ce point de vue-là que c'est beaucoup</span> <span class="audio-time caption">plus pratique en encadrement personnel ou par</span> <span class="audio-time caption">groupe où on peut expliquer des choses qu'on</span> <span class="audio-time caption">pense que les gens ne savent pas et donc ils ne</span> <span class="audio-time caption">vont jamais poser la question à l'IA là-dessus,</span> <span class="audio-time caption">par définition.</span> <p></p><span class="audio-time caption"><strong>Sacha:</strong>  Merci beaucoup à vous deux, à tout le</span> <span class="audio-time caption">monde.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Merci beaucoup pour cette</span> <span class="audio-time caption">organisation, cette deuxième séance.</span> <span class="audio-time caption">Peut-être à une prochaine fois.</span> <span class="audio-time caption">Merci beaucoup en tout cas pour l'organisation, Sacha.</span> <span class="audio-time caption">Au revoir.</span> <p></p><span class="audio-time caption"><strong>Prot:</strong>  À la prochaine, au revoir.</span> <p></p><span class="audio-time caption"><strong>Fabrice:</strong>  Salut, je vais vite dans l'autre.</span></div>
<p></p>


<a name="end-emacs-chat-30-transcript"></a>
</div>
</div>
<div class="outline-3">
<h3><a href="https://sachachua.com/blog/feed/index.xml#emacs-chat-30-fabrice-niessen-en-fran-ais-partie-2-chat">Chat</a></h3>
<div class="outline-text-3">
<div class="chat">
<ul>
<li><span class="nick">protesilaos:</span> ​à bientôt!</li>
<li><span class="nick">julienlambe78:</span> ​Bonjour de Belgique 🙂 Sacha, le volume de votre micro est un peu bas. Nous n'entendons pas bien vos questions. Pensez-vous pouvoir augmenter le volume un petit peu?</li>
<li><span class="nick">SaMusz73:</span>​ ​synlinks `ln -s` et hardlinks `ln ` vers des fichiers sont gérés dans git ? J'ai cru comprendre que git gére assez mal ces symlinks</li>
<li><span class="nick">SaMusz73:</span> Merci :)</li>
<li><span class="nick">JeromeVanLunter:</span> ​​MERCI</li>
<li><span class="nick">SaMusz73:</span> Merci à vous tous !</li>
</ul>

</div>
</div>
</div>
<div><a href="https://sachachua.com/blog/2026/08/27-aout-emacs-chat-fabrice-niessen-en-francais-partie-2/index.org">View Org source for this post</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F08%2F27-aout-emacs-chat-fabrice-niessen-en-francais-partie-2%2F&amp;body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p></body></html>]]></content>
        <author>
            <name>Sacha Chua</name>
            <uri>https://sachachua.com/blog/category/emacs/feed/index.xml</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[James Cherti: The Emacs security settings that might silently compromise your system]]></title>
        <id>https://www.jamescherti.com/emacs-security-settings/</id>
        <link href="https://www.jamescherti.com/emacs-security-settings/"/>
        <updated>2026-09-01T14:53:19.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>Emacs ships with default settings intended for backward compatibility and convenience over strict security. The following settings enforce TLS certificate verification, reduce unintended ffap network lookups, restrict the evaluation of potentially unsafe local file, and directory variables, automated package review system, and protect authentication credentials stored by Emacs.</p>



<h2>Network and communication security</h2>



<p>By default, Emacs prompts the user interactively if a connection appears untrustworthy. You can additionally require certificate validation to fail at the TLS library level, causing invalid certificates to result in a connection error.</p>



<p>Requiring certificate validation at the TLS layer reduces the possibility of <strong>accidentally accepting an invalid certificate in response to an interactive warning</strong>.</p>



<p>Define these variables in your <code>early-init.el</code> to ensure the settings are established before startup code initiates network connections:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> gnutls-verify-error <span class="hljs-literal">t</span>)
(<span class="hljs-name">setq</span> tls-checktrust <span class="hljs-literal">t</span>)
(<span class="hljs-name">setq</span> gnutls-min-prime-bits <span class="hljs-number">3072</span>)</code></span></pre>


<ul>
<li><code>gnutls-verify-error</code>: Controls GnuTLS certificate verification. Setting this to <code>t</code> makes any certificate validation failure fatal.</li>



<li><code>tls-checktrust</code>: Controls external TLS binaries. Setting this to <code>t</code> ensures certificate validation is enforced if Emacs falls back to using external tools.</li>



<li><code>gnutls-min-prime-bits</code>: Defines the minimum acceptable size for Diffie-Hellman key exchange primes. Setting this to 3072 rejects handshakes using primes smaller than 3072 bits.</li>
</ul>



<p>The benefit of strictly enforcing connection security at the TLS layer is preventing the accidental acceptance of invalid certificates or weak cryptography.</p>



<h2>Find file at point network requests</h2>



<p>The <code>ffap</code> (Find File At Point) package scans text around point to identify file paths, URLs, and hostnames. This triggers when you invoke an <code>ffap</code> command, most commonly <code>find-file-at-point</code>, which replaces standard <code>C-x C-f</code> if you have <code>(ffap-bindings)</code> enabled, while your cursor is resting on or near a string of text that resembles a domain name.</p>



<p>Setting <code>ffap-machine-p-known</code> to <code>'reject</code> prevents FFAP from probing hostnames with known domains:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> ffap-machine-p-known 'reject)</code></span></pre>


<p>The tradeoff is that <code>ffap</code> will no longer automatically recognize hostnames as remote connection targets, meaning you must enter remote host paths explicitly.</p>



<h2>Encrypting auth sources</h2>



<p>Emacs' <code>auth-source</code> uses files such as <code>~/.authinfo</code> and <code>~/.netrc</code> to store authentication credentials that other Emacs packages can retrieve when connecting to remote services. For example:</p>



<ul>
<li>Tramp: Passwords for remote server access.</li>



<li>Gnus and Message: Credentials for mail retrieval (IMAP, POP3) and transmission (SMTP).</li>
</ul>



<p>An auth-source entry can contain information such as a hostname, username, password, port, or protocol. Because these files may contain sensitive credentials, storing them as plain text exposes them to any process or user that can read the file.</p>



<p>To enforce the exclusive use of an encrypted file and prevent accidental plain-text credential storage, redefine the authentication sources list:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> auth-sources '(<span class="hljs-string">"~/.authinfo.gpg"</span>))</code></span></pre>


<p>Additional security configurations:</p>



<ul>
<li>To encrypt the file using specific GPG public keys, define the recipients using <code>auth-source-gpg-encrypt-to</code>:</li>
</ul>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> auth-source-gpg-encrypt-to '(<span class="hljs-string">"your.email@example.com"</span>))</code></span></pre>


<ul>
<li>Emacs can cache passwords to minimize prompt interruptions. The cache expiration can be configured using <code>auth-source-cache-expiry</code>:</li>
</ul>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> auth-source-cache-expiry <span class="hljs-number">3600</span>)</code></span></pre>


<h2>Clear auth-source cache when the user is idle</h2>



<p>The <code>auth-source</code> library uses password cache to store authentication data in memory. By default, <code>auth-source-do-cache</code> is enabled (<code>t</code>) and <code>auth-source-cache-expiry</code> is set to 7200 seconds (2 hours). You can restrict this credential exposure using idle timers, targeted cache clearing, or by disabling the cache entirely.</p>



<p>Use an idle timer to purge credentials after a period of inactivity instead of waiting for the absolute cache expiry limit:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">defun</span> my-security-clear-caches ()
  <span class="hljs-string">"Clear all cached authentication data managed by auth-source."</span>
  (<span class="hljs-name">when</span> (<span class="hljs-name">fboundp</span> 'auth-source-forget-all-cached)
    (<span class="hljs-name">auth-source-forget-all-cached</span>)))

(<span class="hljs-name">run-with-idle-timer</span> <span class="hljs-number">900</span> <span class="hljs-literal">t</span> #'my-security-clear-caches)</code></span></pre>


<p>The benefit is that it limits memory exposure to 15 minutes of idle time. The tradeoff is that background operations requiring authentication may fail or block while awaiting a passphrase when returning to the editor.</p>



<h2>Symbol shorthand code execution</h2>



<p>Emacs 28.1 added a feature called symbol shorthands (<code>read-symbol-shorthands</code>). A vulnerability exists in Emacs versions 28.1 through 31 pretest where this feature can be abused to trigger arbitrary code execution simply by opening a specially crafted file. The malicious execution occurs immediately, even before the file contents are displayed.</p>



<p>The exploit relies on Emacs evaluating symbol shorthands during unsafe <code>intern</code> calls in <code>vc-find-backend-function</code> and <code>c-compose-keywords-list</code>. Emacs 31.1 mitigates the vulnerability. Earlier versions, including affected Emacs 31 pretest versions, remain vulnerable unless the mitigation is applied manually.</p>



<p>For users on Emacs 31 pretest or older, you can implement a mitigation by defining an advice function that locally disables symbol shorthands and applying it around the vulnerable operations.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">defun</span> my-suppress-shorthands (<span class="hljs-name">orig</span> <span class="hljs-symbol">&amp;rest</span> args)
  <span class="hljs-string">"Call ORIG function with ARGS while binding `read-symbol-shorthands' to nil.
ORIG is the original function being advised.
ARGS is the list of arguments passed to the original function.
This acts as advice to prevent arbitrary code execution via symbol shorthands
during unsafe operations like interning symbols on file open."</span>
  (<span class="hljs-name">let</span> (<span class="hljs-name">read-symbol-shorthands</span>)
    (<span class="hljs-name">apply</span> orig args)))

<span class="hljs-comment">;; A workaround patch (Commit 8466eb44) was applied to the emacs-31 release</span>
<span class="hljs-comment">;; branch on August 5, 2026. Early pretest versions of Emacs 31 do not</span>
<span class="hljs-comment">;; include this mitigation.</span>
(<span class="hljs-name">when</span> (<span class="hljs-name">&lt;</span> emacs-major-version <span class="hljs-number">32</span>)
  (<span class="hljs-name">advice-add</span> 'vc-find-backend-function <span class="hljs-symbol">:around</span> #'my-suppress-shorthands)

  (<span class="hljs-name">with-eval-after-load</span> 'cc-fonts
    (<span class="hljs-name">advice-add</span> 'c-compose-keywords-list <span class="hljs-symbol">:around</span> #'my-suppress-shorthands)))</code></span></pre>


<p>This protects the editor from silent code execution attacks embedded in untrusted source files without requiring an upgrade to an unreleased Emacs version. The tradeoff is that it modifies core function behavior via advice, though the impact on version control and C/C++ fontification performance is negligible.</p>



<h2>Package Management</h2>



<p>Emacs 31.1 added <code>package-review-policy</code>, an automated package review system, allowing inspection of upstream code changes before installation:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> package-review-policy <span class="hljs-literal">t</span>)</code></span></pre>


<p>Setting <code>package-review-policy</code> to <code>t</code> causes Emacs to require a review of packages before installation or upgrade. The package is unpacked into a temporary review directory, where the source can be compared with the previous installation (diff), and reviewed. The package is installed only if the review succeeds.</p>



<p>The main downside is that updates can no longer run in the background. Having to review diffs by hand gets tedious when upgrading multiple packages at once.</p>



<h2>Securing .dir-locals.el and local variables</h2>



<p>Emacs automatically applies project-specific configurations through file-local and directory-local (<code>.dir-locals.el</code>) variables when opening a file or directory. While this feature ensures consistent settings across environments, it can cause security risks and persistent prompt fatigue when editing source code. Malicious <code>.dir-locals.el</code> files or file-local variables containing <code>eval</code> forms can execute arbitrary Lisp code if Emacs is configured to evaluate them, or if the user explicitly approves the relevant prompt. This article outlines configurations for securing file-local and <code>.dir-locals.el</code> variables:<br><a href="https://www.jamescherti.com/securing-emacs-dir-locals-el-local-variables/"><strong>Emacs .dir-locals.el and Local Variables - Securing and Reducing Prompts</strong></a>.</p>
<div class="yarpp yarpp-related yarpp-related-rss yarpp-template-list">

<h3>Related posts:</h3><ol>
<li><a href="https://www.jamescherti.com/compiling-emacs/">A Technical Guide to Compiling Emacs for Performance on Linux and Unix systems</a></li>
<li><a href="https://www.jamescherti.com/measuring-emacs-startup-time/">Measuring Emacs startup time more accurately than the built-in emacs-init-time</a></li>
<li><a href="https://www.jamescherti.com/securing-emacs-dir-locals-el-local-variables/">Securing Emacs .dir-locals.el and local variables</a></li>
<li><a href="https://www.jamescherti.com/emacs-compile-angel-byte-native-compile/">The compile-angel Emacs package: Byte-compile and Native-compile Emacs Lisp libraries Automatically</a></li>
<li><a href="https://www.jamescherti.com/emacs-ultisnips-mode-edit-snippets-files/">ultisnips-mode.el - An Emacs major mode for editing Ultisnips snippet files (*.snippets files)</a></li>
<li><a href="https://www.jamescherti.com/easysession-el-persist-restore-emacs-session/">easysession.el: Easily persist and restore Emacs sessions (windows, tab-bar, file buffers, scratch, Dired, narrowing, indirect buffers/clones, Magit buffers...); a robust desktop.el replacement</a></li>
<li><a href="https://www.jamescherti.com/emacs-python-dev-using-eglot-pylsp-ruff-pylint-flake8/">Eglot for Python Development in Emacs: Integrating python-lsp-server (pylsp) with Linters and Formatters</a></li>
</ol>
</div>
</body></html>]]></content>
        <author>
            <name>James Cherti</name>
            <uri>https://www.jamescherti.com</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Irreal: Wordcraft]]></title>
        <id>https://irreal.org/blog/?p=14053</id>
        <link href="https://irreal.org/blog/?p=14053"/>
        <updated>2026-09-01T14:29:01.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
I do a <i>lot</i> of writing, almost all of it in Emacs so I’m always happy to see how other people use our editor of choice for writing. The latest example I’ve found is from JTR over at <a href="https://taonaw.com">The Art Of Not Asking Why</a>. His post considers some <a href="https://taonaw.com/2026/08/29/emacs-config-gems-part.html#fnref:6">simple expansion functions in Emacs</a>.
</p>
<p>
He starts off with dictionaries. As many of you know, I’ve been a Webster 1913 dictionary user ever since <a href="https://irreal.org/blog/?p=4190">James Somers explained why I (and you) should be</a>. I have gone through many iterations of integrating it into Emacs but have converged on the same solution that JTR uses: the built in <code>dictionary-search</code> function built into Emacs that we both have configured to use the online collection <code>dict.org</code>, although I still have a local copy from my previous iterations.
</p>
<p>
The <code>dict.org</code> collection is nice because it contains several dictionaries besides Webster’s, and <code>dictionary-search</code> will return entries from all of them that have the word you’re looking for. The best part is that it’s trivial to set up You don’t have to download dictionaries or packages. You simply set a variable saying you want to use <code>dict.org</code> and that’s it. There <i>are</i> some nuances with that so be sure to read JTR’s post or the documentation.
</p>
<p>
JTR’s post is worth reading just for his dictionary advice but there’s more.
</p>
<p>
Next he briefly discusses Hippie Expand, a way of completing words based on a list of functions that you can specify. I’ve been using it for a long time, but his list of completion functions was more complete than mine so I stole it.
</p>
<p>
Finally, he considers ispell. If you’ve been following along, you’ll know that I recently started using <a href="https://github.com/minad/jinx">Jinx</a> instead of ispell but JTR showed me a couple of things I didn’t know about. I’ve always used ispell with flyspell so I never learned some of its extra tricks. One of those is a sort of fuzzy search for a word you’re not sure how to spell. That happens to me a lot so I was glad to find out about ispell’s ability to handle it.
</p>
<p>
The other thing you can do with ispell is to use it as a quick way of doing a replace. Take a look at JTR’s post for more on both those functions.
</p>
<p>
JTR’s post is from last week so Sacha will doubtless cover it before you see this but I had a lot to say about it so I decided to write about it anyway. It’s definitely worth a careful reading.</p>
</body></html>]]></content>
        <author>
            <name>Irreal</name>
            <uri>https://irreal.org/blog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Jeremy Friesen: How I’m Feeling Emacs Command]]></title>
        <id>https://takeonrules.com/2026/09/01/how-im-feeling-emacs-package/</id>
        <link href="https://takeonrules.com/2026/09/01/how-im-feeling-emacs-package/"/>
        <updated>2026-09-01T10:35:14.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>In late April of this year, I’ve began experiencing a heightened anxiety.  I
started therapy then later taking anti-anxiety medication.  I worked to change
some of my behaviors.  And started writing even more personal journal entries.</p>
<p><time>Last Friday</time> I found a list of feelings and responses that resonated with me:</p>
<ul>
<li><strong>Angry:</strong> Lift weights</li>
<li><strong>Stressed:</strong> Go for a walk</li>
<li><strong>Procrastinating:</strong> Set a 10-minute timer</li>
<li><strong>Sad:</strong> Get sunlight</li>
<li><strong>Can’t focus:</strong> Clean your workspace</li>
<li><strong>Negative thoughts:</strong> Write 3 gratitudes</li>
<li><strong>Stuck:</strong> Change your environment</li>
<li><strong>Financial stress:</strong> Build an emergency fund</li>
<li><strong>Low energy:</strong> Fix your sleep</li>
<li><strong>Overthinking:</strong> Journal it out</li>
<li><strong>Lonely:</strong> Call someone</li>
<li><strong>No motivation:</strong> Start with 2 minutes</li>
<li><strong>Anxiety:</strong> Slow your breathing</li>
<li><strong>Brain fog:</strong> Drink water and move</li>
<li><strong>Low confidence:</strong> Keep small promises</li>
<li><strong>Lost:</strong> Define one clear goal</li>
</ul>
<p>Twice I’ve looked to that list and found the emotions I was feeling and took
action from the responses.  The second time I found myself digging back through
my journal to find the entry and pick how I was feeling.</p>
<p>And I realized, that I could encode this as an <span><a href="https://en.wikipedia.org/wiki/Emacs">Emacs</a></span> <small><a href="https://takeonrules.com/site-map/glossary/#abbr-dfn-GLOSSARY-EMACS">📖</a></small>
 function and when I found
myself needing to interrogate my “feels” I could type <code>M-x how-im-feeling</code> and
get a set of responses I could then use to take action.</p>
<p>Here’s the <code>how-im-feeling</code> command and supporting variable:</p>
<pre><code class="language-emacs-lisp">(defvar how-im-feeling-responses
  '(("Angry" . "Lift weights")
    ("Stressed" . "Go for a walk")
    ("Procrastinating" . "Set a 10-minute timer")
    ("Sad" . "Get sunlight")
    ("Can't focus" . "Clean your workspace")
    ("Negative thoughts" . "Write 3 gratitudes")
    ("Stuck" . "Change your environment")
    ("Financial stress" . "Build an emergency fund")
    ("Low energy" . "Fix your sleep")
    ("Overthinking" . "Journal it out")
    ("Lonely" . "Call someone")
    ("No motivation" . "Start with 2 minutes")
    ("Anxiety" . "Slow your breathing")
    ("Brain fog" . "Drink water and move")
    ("Low confidence" . "Keep small promises")
    ("Lost" . "Define one clear goal"))
  "How I'm feeling and a simple response to move with that feeling")

(defun how-im-feeling ()
  "Prompt for how I'm feeling and respond with what to do."
  (interactive)
  (let ((feels
         (completing-read-multiple "I'm Feeling: "
                                   how-im-feeling-responses
                                   #'completing-read-omit-p
                                   t))
        (concatter
         (lambda (feel)
           (format "- *%s:* %s"
                   feel
                   (alist-get feel
                              how-im-feeling-responses
                              nil nil #'string=)))))
    (insert
     (format "I'm feeling:\n\n%s\n"
             (mapconcat concatter feels "\n")))))

(defun completing-read-omit-p (thusfar)
  "Omit completions THUSFAR given."
  (let ((input
         (butlast (split-string
                   (minibuffer-contents-no-properties)
                   crm-separator)))
        (test
         (car (last (split-string thusfar crm-separator)))))
    (not (member test input))))
</code></pre>
<h2>Wrapping Up</h2>
<p>Now, when I’m feeling the feels, I have another strategy for approaching and
addressing those feels.  The prompts don’t provide direct answers but instead
nudge me towards moving with and through those feels.  All in service of helping
me cope with anxiety.</p>

      </body></html>]]></content>
        <author>
            <name>Jeremy Friesen</name>
            <uri>https://takeonrules.com/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Games: The Emacs Carnival Post For September 2026]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/09/games-emacs-carnival-post-for-september.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/09/games-emacs-carnival-post-for-september.html"/>
        <updated>2026-09-01T04:00:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>I don't play games within Emacs.  Instead I enjoy learning about the
many functions and modes it has.  I even derived a <a href="https://ray-on-emacs.blogspot.com/2026/07/emacs-tip-of-day-in-popup-frame.html">Tip of the Day</a>
that displays help on a randomly-chosen function.  I invoke it at
startup; there's a non-zero probability that I'll spend 15 minutes
or so exploring the function it provides.
</p>

<p>
I get the impression that I'm not the only one who enjoys Emacs this
way.  When I read others' postings, I see references to "customize,"
"configure," "init file," "rabbit hole."  I'm pretty sure I also came
across a few posts that contained this: "<em>my ex-spouse made me chose
    between him/her and Emacs</em>."  Often, reading these posts is enjoyable
in itself.
</p>

<p>
I enjoy writing and blogging, too.  I started my first journal when
I was 13 years old.  My answer to "What book would you choose if
stranded on an uninhabited island?<sup>1</sup>" would be "a blank notebook that
comes with a pencil."  I also enjoy interacting with the Emacs
community.  It's as if this "Ray on Emacs" blog provides
entertainment squared.  This can be expressed mathematically as <code>E<sup>2</sup></code>,
where <code>E</code> is entertainment and <code>2</code> is two.
</p>

<p>
While researching this topic, I was surprised at how many games we
can choose from, even in plain vanilla.  Naive folks might scoff at
programmers who write games for Emacs.  But they don't understand
that games demonstrate Emacs' capabilities.  I suspect the
underlying motivation is that writing games for Emacs is fun.  It's
a challenge that leads to accomplishment.<sup>2</sup>
</p>

<p>
I'm looking forward to reading submissions to <a href="https://www.emacswiki.org/emacs/CarnivalSeptember2026" target="_blank">Emacs Carnival September 2026</a> and trying
some games.  participate is Humor not to necessary.<sup>3</sup>
</p>

<p>
  <sup>1</sup> aka "Desert Island" see <a href="https://en.wikipedia.org/wiki/Uninhabited_island#bodyContent" target="_blank">Uninhabited island on Wikipedia</a>
</p>

<p>
  <sup>2</sup> Folks in the MS-DOS USENETs would joke about writing Batch Files
that could do calculus.  At least I think it was a joke.
</p>

<p>
  <sup>3</sup> "Humor is not necessary to participate" (as evidenced
  by this post).</p>
  
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Sacha Chua: 2026-08-31 Emacs news]]></title>
        <id>https://sachachua.com/blog/2026/08/2026-08-31-emacs-news/</id>
        <link href="https://sachachua.com/blog/2026/08/2026-08-31-emacs-news/"/>
        <updated>2026-08-31T19:23:36.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
Lexical binding: If you've recently updated to Emacs 31, you might have gotten warnings about <code>lexical-binding</code> in your packages or personal Emacs Lisp configuration. You can check your packages to see if the package authors have updated them. For your personal Emacs Lisp configuration, consider adding
</p>


<div class="org-src-container">
<pre><code><span class="org-comment-delimiter">;;; </span><span class="org-comment">-*- lexical-binding: t -*-</span>
</code></pre>
</div>


<p>
to the top line. If that makes your code stop working correctly when you restart Emacs, set it to <code>nil</code> instead with:
</p>


<div class="org-src-container">
<pre><code><span class="org-comment-delimiter">;;; </span><span class="org-comment">-*- lexical-binding: nil -*-</span>
</code></pre>
</div>


<p>
to get it to work for now. Additional resources:
</p>
<ul>
<li><a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Selecting-Lisp-Dialect.html">Selecting Lisp Dialect</a> in the Elisp info manual</li>
<li><a href="https://yoo2080.wordpress.com/2011/12/31/lexical-scoping-and-dynamic-scoping-in-emacs-lisp/">Lexical scoping and dynamic scoping in Emacs Lisp | Yoo Box</a></li>
</ul>

<p>
Emacs Carnival: Check out the entries for August in <a href="https://www.chiply.dev/post-august-emacs-carnival">"The Search for Knowledge"</a> and stay tuned for September's topic, "Games."
</p>

<ul>
<li>Emacs 31:
<ul>
<li><a href="https://github.com/emacs-mirror/emacs/blob/emacs-31.1/etc/NEWS">Emacs 31.1 is released!</a> (<a href="https://www.reddit.com/r/emacs/comments/1vwziq6/emacs_311_is_released/">Reddit</a>, <a href="https://news.ycombinator.com/item?id=49385296">HN</a>, <a href="https://news.ycombinator.com/item?id=49335485">HN</a>, <a href="https://news.ycombinator.com/item?id=49341172">HN</a>, <a href="https://irreal.org/blog/?p=14021">Irreal</a>, <a href="https://lobste.rs/s/rehaa3/emacs_31_1_released">lobste.rs</a>) - including this again from last week</li>
<li><a href="https://www.masteringemacs.org/article/whats-new-in-emacs-311">Mickey Petersen: What's New in Emacs 31.1?</a> (<a href="https://www.reddit.com/r/emacs/comments/1vx04o6/whats_new_in_emacs_311/">Reddit</a>, <a href="https://news.ycombinator.com/item?id=49419252">HN</a>) - including this again from last week</li>
<li><a href="https://lists.gnu.org/archive/html/emacs-devel/2026-08/msg00778.html">Android builds of Emacs 31.1 now available</a></li>
<li><a href="https://lists.gnu.org/archive/html/emacs-devel/2026-08/msg00775.html">Emacs 31.1 binaries now available for Windows</a> (<a href="https://www.reddit.com/r/emacs/comments/1vxroqw/emacs_311_binaries_now_available_for_windows/">Reddit</a>)</li>
</ul></li>
<li>Upcoming events (<a href="https://emacslife.com/calendar/emacs-calendar.ics">iCal file</a>, <a href="https://emacslife.com/calendar/">Org</a>):
<ul>
<li>EmacsATX: Emacs Social <a href="https://www.meetup.com/emacsatx/events/316121976/">https://www.meetup.com/emacsatx/events/316121976/</a> Thu Sep 3 1600 America/Vancouver - 1800 America/Chicago - 1900 America/Toronto - 2300 Etc/UTC – Fri Sep 4 0100 Europe/Berlin - 0430 Asia/Kolkata - 0700 Asia/Singapore</li>
<li>M-x Research: TBA <a href="https://m-x-research.github.io/">https://m-x-research.github.io/</a> Fri Sep 4 0800 America/Vancouver - 1000 America/Chicago - 1100 America/Toronto - 1500 Etc/UTC - 1700 Europe/Berlin - 2030 Asia/Kolkata - 2300 Asia/Singapore</li>
<li>Emacs.si (in person): Emacs.si meetup #9 2026 (v #živo) <a href="https://dogodki.kompot.si/events/8711d3f8-b0d7-48bf-a64b-8cd48eed728c">https://dogodki.kompot.si/events/8711d3f8-b0d7-48bf-a64b-8cd48eed728c</a> Mon Sep 7 1900 CET</li>
<li>London Emacs (in person): Emacs London meetup <a href="https://www.meetup.com/london-emacs-hacking/events/316294538/">https://www.meetup.com/london-emacs-hacking/events/316294538/</a> Tue Sep 8 1800 Europe/London</li>
<li>Emacs Berlin: In-Person-Only Emacs-Berlin Stammtisch <a href="https://emacs-berlin.org/">https://emacs-berlin.org/</a> Tue Sep 8 1900 Europe/Berlin</li>
<li>OrgMeetup (virtual) <a href="https://orgmode.org/worg/orgmeetup.html">https://orgmode.org/worg/orgmeetup.html</a> Wed Sep 9 0900 America/Vancouver - 1100 America/Chicago - 1200 America/Toronto - 1600 Etc/UTC - 1800 Europe/Berlin - 2130 Asia/Kolkata – Thu Sep 10 0000 Asia/Singapore</li>
<li>Atelier Emacs Montpellier (in person) <a href="https://lebib.org/date/atelier-emacs">https://lebib.org/date/atelier-emacs</a> Fri Sep 11 1800 Europe/Paris</li>
</ul></li>
<li>Beginner:
<ul>
<li><a href="https://sachachua.com/blog/2026/04/what-s-in-the-emacs-newcomers-presets-theme/">[Article] What's in the Emacs 31 newcomers-presets theme? by Sacha Chua</a> (<a href="https://www.reddit.com/r/emacs/comments/1vy4hbc/article_whats_in_the_emacs_31_newcomerspresets/">Reddit</a>)</li>
<li><a href="https://www.youtube.com/watch?v=OOuP9YCQnpE">No instales otro editor hasta ver esta guía de Emacs en Linux</a> (17:25)</li>
<li><a href="https://www.youtube.com/watch?v=AGfqCsk-C7k">Introduction to the GOAT IDE - Vanilla Emacs</a> (14:09)</li>
</ul></li>
<li>Emacs configuration:
<ul>
<li><a href="http://en.andros.dev/blog/dc7db35a/how-i-organize-my-emacs-configuration/">Andros Fenollosa: How I organize my Emacs configuration</a> (<a href="https://irreal.org/blog/?p=14046">Irreal</a>)</li>
<li><a href="https://chrismaiorana.com/dont-go-bankrupt-go-local/">Chris Maiorana: Don’t go bankrupt, go local</a> (<a href="https://irreal.org/blog/?p=14048">Irreal</a>)</li>
<li><a href="https://www.jamescherti.com/securing-emacs-dir-locals-el-local-variables/">James Cherti: Securing and reducing prompts for Emacs .dir-locals.el and local variables</a></li>
<li><a href="https://www.jamescherti.com/measuring-emacs-startup-time/">Measuring Emacs Startup Time More Accurately Than the Built-in emacs-init-time Function</a> (<a href="https://www.reddit.com/r/emacs/comments/1w0si4i/measuring_emacs_startup_time_more_accurately_than/">Reddit</a>)</li>
<li><a href="https://github.com/Dspil/guard.el">Guard.el on Melpa</a> (<a href="https://www.reddit.com/r/emacs/comments/1vztanu/guardel_on_melpa/">Reddit</a>)</li>
</ul></li>
<li>Emacs Lisp:
<ul>
<li><a href="https://mastodon.fixermark.com/@mark/117158495805155725">Tip about using unread-command-events to simulate keyboard input (@mark@mastodon.fixermark.com)</a></li>
<li><a href="https://www.youtube.com/watch?v=DkVimqsvuwQ">Doom Emacs: debugging a yasnippet issue with edebug</a> (01:16:31)</li>
<li><a href="https://www.youtube.com/watch?v=C5hkOAU0Ahk">Emacs Warning (files): Missing ‘lexical-binding’ cookie</a> (19:34)</li>
</ul></li>
<li>Appearance:
<ul>
<li><a href="https://git.etenil.net/cursor-breathe-mode.el/about/">cursor-breathe-mode.el - animate cursor and make it "breathe"</a> (<a href="https://toot.cat/@etenil/117156059295173469">@etenil@toot.cat</a>)</li>
<li><a href="https://github.com/zHaOdANiuu/minibuffer-frame">New Package (minibuffer-frame)</a> (<a href="https://www.reddit.com/r/emacs/comments/1vwucmh/new_package_minibufferframe/">Reddit</a>)</li>
<li><a href="https://www.jamescherti.com/emacs-scrolling-better-performance-usability/">Configuring Emacs Scrolling for Better Performance and Usability</a> (<a href="https://www.reddit.com/r/emacs/comments/1vz1uc4/configuring_emacs_scrolling_for_better/">Reddit</a>, <a href="https://irreal.org/blog/?p=14042">Irreal</a>)</li>
<li><a href="https://www.youtube.com/watch?v=1dlPRw60I0A">Larp. Emacs  Rice 🦄</a> (12:45, and also <a href="https://www.youtube.com/watch?v=OtR2ER9aOmw">Larp. Emacs Rice FIX 🤎📜🧸</a> 03:11)</li>
</ul></li>
<li>Navigation:
<ul>
<li><a href="https://github.com/benleis1/emacs-init/blob/458719e7911337230b9ab5c46a95e234996d40e3/imenu.el#L206">Improved hierarchical imenu entries for elisp - fold defun/use-package under sections</a> (<a href="https://mathstodon.xyz/@benleis/117186862457099332">@benleis@mathstodon.xyz</a>)</li>
<li><a href="https://github.com/jamescherti/kirigami.el">kirigami.el 1.2.0 - A unified way to fold and unfold code and text in Emacs​</a> (<a href="https://www.reddit.com/r/emacs/comments/1w2mroq/kirigamiel_a_unified_way_to_fold_and_unfold_code/">Reddit</a>)</li>
<li><a href="https://github.com/jamescherti/easysession.el/">easysession.el 1.3.0: Persist and Restore Emacs sessions​</a> (<a href="https://www.reddit.com/r/emacs/comments/1w1168c/easysessionel_persist_and_restore_emacs_sessions/">Reddit</a>)</li>
</ul></li>
<li>Hyperbole:
<ul>
<li><a href="https://www.chiply.dev/post-hyperbole-hyrolo">Hyperbole HyRolo: Search, Retrieve and Insert Records, Not Lines</a> (<a href="https://www.youtube.com/watch?v=5Oo_KqVcLFM">YouTube</a> 25:47, (<a href="https://www.reddit.com/r/emacs/comments/1w06ab6/hyperbole_hyrolo_search_retrieve_and_insert/">Reddit</a>)</li>
</ul></li>
<li>Dired:
<ul>
<li><a href="https://www.jamescherti.com/emacs-dired-configuration/">James Cherti: Fixing Emacs Dired Defaults: Settings for Better File Management</a> (<a href="https://www.reddit.com/r/emacs/comments/1w1pyhs/practical_emacs_dired_tweaks/">Reddit</a>)</li>
</ul></li>
<li>Writing:
<ul>
<li><a href="https://taonaw.com/2026/08/29/emacs-config-gems-part.html">TAONAW - Emacs and Org Mode: Emacs Config Gems - Part 5</a> dict, hippie-expand, ispell</li>
<li><a href="https://rahuljuliato.com/posts/markdown-ts-mode-emacs-31">An unofficial guide to markdown-ts-mode on Emacs 31</a> (<a href="https://www.reddit.com/r/emacs/comments/1vzcv1a/an_unofficial_guide_to_markdowntsmode_on_emacs_31/">Reddit</a>, <a href="https://news.ycombinator.com/item?id=49464543">HN</a>)</li>
<li><a href="https://www.youtube.com/watch?v=I4zn3NTpe5c">Emacs Treesitter Markdown Mode - First Impressions</a> (17:47)</li>
<li><a href="https://github.com/laserattack/emado">mado/emado update: static binaries, fuzzy matching, and more</a> (<a href="https://www.reddit.com/r/emacs/comments/1vz3dkh/madoemado_update_static_binaries_fuzzy_matching/">Reddit</a>) - markdown organizer for Emacs</li>
<li><a href="https://emacsredux.com/blog/2026/08/26/meet-utterson-my-jekyll-blogging-helper/">Emacs Redux: Meet Utterson, my Jekyll blogging helper</a></li>
<li><a href="https://irreal.org/blog/?p=14040">Irreal: Fixing Define-word</a></li>
</ul></li>
<li>Denote:
<ul>
<li><a href="https://donovan-ratefison.mg/2026/08/29/My-way-of-handling-knowledge-Emacs-Carnival-The-Search-for-Knowledge/">Donovan R.: My way of handling knowledge (Emacs Carnival - The Search for Knowledge)</a></li>
</ul></li>
<li>Org Mode:
<ul>
<li><a href="https://list.orgmode.org/87v791m6be.fsf@localhost">Org Mode requests: [FR] Include :var expansion in expanded noweb references (was: ob-clojure :var header argument not work when src block is noweb called by another src block)</a></li>
<li><a href="https://sachachua.com/blog/2026/08/emacs-carnival-aug-2026-information-management-and-knowledge-graphs/">Emacs Carnival Aug 2026: Information management and knowledge graphs</a>
<ul>
<li><a href="https://sachachua.com/blog/2026/08/carnaval-d-emacs-d-aout-la-gestion-d-information-et-les-graphes-de-connaissances/">Carnaval d'Emacs d'août 2026 : la gestion d'information et les graphes de connaissances</a></li>
</ul></li>
<li><a href="https://social.tchncs.de/@stackeffect/117177294677413093">Automatically insert a checkbox into the new Org list item if the current item has one, even with M-RET (org-meta-return) (@stackeffect@social.tchncs.de)</a> (acts like M-S-RET, org-insert-todo-heading)</li>
<li><a href="https://v.redd.it/jye97f4f1flh1">Updated my ob-glsl module to use Emacs 32 Canvas for animation</a> (<a href="https://www.reddit.com/r/emacs/comments/1vxkrx3/updated_my_obglsl_module_to_use_emacs_32_canvas/">Reddit</a>)</li>
<li>Org development:
<ul>
<li><a href="https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/etc/ORG-NEWS?id=b75b790398bb3a0cbd7d25a5a2e6237e73c0bc03">lisp/org-capture.el: Accept omitted or nil headline for the file+headline</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/etc/ORG-NEWS?id=8a858faeca8b3654fd5880f3991a15b55f20c491">lisp/org-capture.el: Accept omitted or nil olp for file+olp+datetree and file+olp</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/etc/ORG-NEWS?id=5e8cc6ce84b2cacb0025e958ffcfce31c1cd96a9">org: Add org-submit-feature-request and org-submit-patch commands</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs/org-mode.git/commit/etc/ORG-NEWS?id=d14d0c36767e614e971621cdc572d329121d21cb">ol: Add new custom variable org-link-preview-include-descriptive</a></li>
</ul></li>
</ul></li>
<li>Completion:
<ul>
<li><a href="https://protesilaos.com/codelog/2026-08-29-emacs-completion-preview-mode/">Protesilaos: Emacs: completion-preview-mode and the Completions buffer (Emacs 31)</a> (<a href="https://www.youtube.com/watch?v=8V4ZyEL_i-s">YouTube</a> 09:18, <a href="https://www.reddit.com/r/emacs/comments/1w1pnpe/prot_vod_emacs_completionpreviewmode_and_the/">Reddit</a>)</li>
<li><a href="https://social.rossabaker.com/@ross/117191554041520170">Silence the bell during minibuffer completions that may not be strict - minibuffer-completion-help (@ross@rossabaker.com)</a></li>
<li><a href="https://www.reddit.com/r/emacs/comments/1w0wcpo/snippets_completion_read/">Snippets + completion read</a></li>
</ul></li>
<li>Coding:
<ul>
<li><a href="https://www.jamescherti.com/emacs-python-dev-using-eglot-pylsp-ruff-pylint-flake8/">Configuring Python Development in Emacs: Eglot, Pylsp, and Dynamic Ruff Integration</a> (<a href="https://www.reddit.com/r/emacs/comments/1vzys57/configuring_python_development_in_emacs_eglot/">Reddit</a>)</li>
<li><a href="https://www.jamescherti.com/emacs-eglot-performance/">Configuring Emacs Eglot for Optimal Performance</a> (<a href="https://www.reddit.com/r/emacs/comments/1vy5i1s/configuring_emacs_eglot_for_optimal_performance/">Reddit</a>)</li>
<li><a href="https://mastodon.social/@jamescherti/117169307557333673">How to remove Eglot from the mode line (@jamescherti)</a></li>
<li><a href="https://github.com/purcell/envrc/pull/130">envrc.el package - Rewrite to support optional async or time-limited direnv invocation by purcell · Pull Request #130 · purcell/envrc · GitHub</a> (<a href="https://hachyderm.io/@sanityinc/117169270533709896">@sanityinc@hachyderm.io</a>)</li>
<li><a href="https://metaredux.com/posts/2026/08/29/smarter-form-targeting-is-not-coming-to-cider.html">Meta Redux: Smarter Form Targeting Is Not Coming to CIDER</a> (<a href="https://metaredux.com/posts/2026/08/26/smarter-form-targeting-is-coming-to-cider.html">previous</a>)</li>
</ul></li>
<li>Shells:
<ul>
<li><a href="https://chaos.social/@citizen428/117179588220817352">Tip about using -a (–alternate-editor) so you can use emacsclient even without having started daemon mode (@citizen428@chaos.social)</a></li>
<li><a href="https://www.reddit.com/r/emacs/comments/1w2ibbx/what_kind_of_wizardry_is_possible_in_mx_shell/">What kind of wizardry is possible in M-x shell?</a></li>
</ul></li>
<li>Web:
<ul>
<li><a href="https://git.andros.dev/andros/kagi-search.el">andros/kagi-search.el: Browse Kagi search results inside Emacs, in a plain buffer, without a graphical browser and without JavaScript</a> (<a href="https://activity.andros.dev/@andros/statuses/01M17DPYNAS2BJVTYCGY5DVVYC">@andros@activity.andros.dev</a>)</li>
<li><a href="http://github.com/dmgerman/browser-gt">browsel has been renamed to browser-gt and is now available on MELPA (v0.95).</a> (<a href="https://www.reddit.com/r/emacs/comments/1vy6pu7/browsel_has_been_renamed_to_browsergt_and_is_now/">Reddit</a>)</li>
</ul></li>
<li>Mail, news, and chat:
<ul>
<li><a href="http://en.andros.dev/blog/5fcf45a2/a-year-of-org-social-my-social-network/">Andros Fenollosa: A year of Org Social, my social network</a></li>
</ul></li>
<li>Multimedia:
<ul>
<li><a href="https://github.com/bartbunting/emacsvox">bartbunting/emacsvox: An experiment in modernization of Emacspeak · GitHub</a> (<a href="https://tweesecake.social/@pixelate/117167664502211508">@pixelate@tweesecake.social</a>)</li>
<li><a href="http://yummymelon.com/devnull/announcing-shazam-el.html">Charles Choi: Announcing shazam.el</a> (<a href="https://www.reddit.com/r/emacs/comments/1vxfzjl/announcing_shazamel_macos_only/">Reddit</a>)</li>
</ul></li>
<li>Fun:
<ul>
<li><a href="https://www.reddit.com/r/emacs/comments/1w07wyt/rfc_cardgames_melpa_thirtyfive_single_and/">RFC: card-games (MELPA) - thirty-five single and multi-player games for emacs (1.0.92:rc2)</a></li>
<li><a href="https://www.reddit.com/r/emacs/comments/1vy0tp3/uniline_goes_25_dimensions_followup/">Uniline goes 2.5 dimensions - followup</a></li>
</ul></li>
<li>LLMs:
<ul>
<li><a href="https://github.com/seewhydee/dsh-emacs-bridge/">seewhydee/dsh-emacs-bridge: Deepseek Harness to Emacs bridge · GitHub</a> (<a href="https://kopiti.am/@cyd/117161275620947448">@cyd@kopiti.am</a>)</li>
<li><a href="https://www.youtube.com/watch?v=zcTzwIdqaj8">Using LLMs to make the Emacs Web Browser Great Again</a> (16:32)</li>
<li><a href="https://github.com/isamert/ellm.el">ellm - Plain text LLM agent</a> (<a href="https://www.reddit.com/r/emacs/comments/1w2pfud/ellm_plain_text_llm_agent/">Reddit</a>)</li>
<li><a href="https://github.com/fstilman/openclaw-sessions">[New package] openclaw-sessions: monitor multiple OpenClaw TUI sessions from Emacs</a> (<a href="https://www.reddit.com/r/emacs/comments/1vzcgbx/new_package_openclawsessions_monitor_multiple/">Reddit</a>)</li>
</ul></li>
<li>Community:
<ul>
<li><a href="https://www.reddit.com/r/emacs/comments/1vxnxgk/fortnightly_tips_tricks_and_questions_20260825/">Fortnightly Tips, Tricks, and Questions — 2026-08-25 / week 34</a></li>
<li><a href="https://www.youtube.com/watch?v=PgBttBe34rw">why I prefer emacs over vim (for writing &amp; lisp programming)</a> (06:50)</li>
<li><a href="https://www.youtube.com/watch?v=4m0G_ASLUXY">Emacs Chat 30: Fabrice Niessen (en français, partie 2)</a> (01:05:56)</li>
</ul></li>
<li>Other:
<ul>
<li><a href="https://codeberg.org/Viiru/caffeinated-compile.el/src/branch/main/caffeinated-compile.el">caffeinated-compile: prevent sleeping on MacOS</a> (<a href="https://corteximplant.com/@Viiru/117177446267026129">@Viiru@corteximplant.com</a>)</li>
<li><a href="https://mbork.pl/2026-08-24_Transforming_yanked_text">Marcin Borkowski: Transforming yanked text</a></li>
</ul></li>
<li>Emacs development:
<ul>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=f03523c1eb3c2e886e1c9e15e93bfb378f216333">Recognize comment-prefixed headings in outlines</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=0899936ea1342c3db104217e0a02a03fbe167beb">Mark timezone-world-timezones obsolete</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=6dfaa91c28e4ec81e35b1a3445b7720cb9c18375">Mark math-tzone-name obsolete</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=da804bbdd5703193424218c337085fcede69b29a">; * etc/NEWS: Announce IME support on w32 TTY frames.  (Bug#81495)</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=8a287a3888cc5c22ac8ae9fbf22d62819af1354d">viper-ex: Implement z command</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=b28750b822dc45ffdceda3ea44a3c8b93f4e5a6b">; * etc/NEWS: Announce addition of 'uuid' library.</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=e156410d3075807604e13df733b300a77fdc7196">Add button to diff failed erts-file test output in ERT</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=60d0df24d323867da7a834bfebaed2078b03c3f4">* lisp/dired.el (dired-jump-map): Remove key 'j' bound to 'dired-jump'.</a></li>
</ul></li>
<li>New packages:
<ul>
<li><a target="_blank" href="https://melpa.org/#/consult-magit">consult-magit</a>: Switch and manage magit buffers with consult (MELPA)</li>
<li><a target="_blank" href="https://elpa.gnu.org/packages/cperl-mode.html">cperl-mode</a>: Perl code editing commands (GNU ELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/eglotx">eglotx</a>: Native LSP multiplexer for Eglot (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/full-gtd">full-gtd</a>: Complete Getting Things Done (GTD) workflow for org-mode (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/gptel-inline">gptel-inline</a>: Persistent gptel session that follows you around (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/latex-to-svg-backend">latex-to-svg-backend</a>: Content-addressed LaTeX-to-SVG image rendering (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/magent">magent</a>: AI coding agent (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/org-draw">org-draw</a>: Draw into Org from a browser (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/org-relative-date">org-relative-date</a>: Live relative-date overlays on org timestamps (MELPA)</li>
<li><a target="_blank" href="https://elpa.nongnu.org/nongnu/project-nix-store.html">project-nix-store</a>: Project backend for Nix-like store (NonGNU ELPA)</li>
</ul></li>
</ul>

<p>
Links from <a href="https://www.reddit.com/r/emacs">reddit.com/r/emacs</a>, <a href="https://www.reddit.com/r/orgmode">r/orgmode</a>, <a href="https://www.reddit.com/r/spacemacs">r/spacemacs</a>, <a href="https://mastodon.social/tags/emacs">Mastodon #emacs</a>, <a href="https://bsky.app/hashtag/emacs">Bluesky #emacs</a>, <a href="https://hn.algolia.com/?query=emacs&amp;sort=byDate&amp;prefix&amp;page=0&amp;dateRange=all&amp;type=story">Hacker News</a>, <a href="https://lobste.rs/search?q=emacs&amp;what=stories&amp;order=newest">lobste.rs</a>, <a href="https://programming.dev/c/emacs?dataType=Post&amp;page=1&amp;sort=New">programming.dev</a>, <a href="https://lemmy.world/c/emacs">lemmy.world</a>, <a href="https://lemmy.ml/c/emacs?dataType=Post&amp;page=1&amp;sort=New">lemmy.ml</a>, <a href="https://planet.emacslife.com">planet.emacslife.com</a>, <a href="https://www.youtube.com/playlist?list=PL4th0AZixyREOtvxDpdxC9oMuX7Ar7Sdt">YouTube</a>, <a href="http://git.savannah.gnu.org/cgit/emacs.git/log/etc/NEWS">the Emacs NEWS file</a>, <a href="https://emacslife.com/calendar/">Emacs Calendar</a>, and <a href="https://lists.gnu.org/archive/html/emacs-devel/2026-08">emacs-devel</a>. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at <a href="mailto:sacha@sachachua.com">sacha@sachachua.com</a>. Thank you!
</p>
<div><a href="https://sachachua.com/blog/2026/08/2026-08-31-emacs-news/index.org">View Org source for this post</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F08%2F2026-08-31-emacs-news%2F&amp;body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p></body></html>]]></content>
        <author>
            <name>Sacha Chua</name>
            <uri>https://sachachua.com/blog/category/emacs/feed/index.xml</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[James Cherti: Securing Emacs .dir-locals.el and local variables]]></title>
        <id>https://www.jamescherti.com/securing-emacs-dir-locals-el-local-variables/</id>
        <link href="https://www.jamescherti.com/securing-emacs-dir-locals-el-local-variables/"/>
        <updated>2026-08-31T17:34:49.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>Emacs automatically applies project-specific configurations through file-local and directory-local (<code>.dir-locals.el</code>) variables when opening a file or directory. While this feature ensures consistent settings across environments, it can cause security risks and persistent prompt fatigue when editing source code. Malicious <code>.dir-locals.el</code> files or file-local variables containing <code>eval</code> forms can execute arbitrary Lisp code if Emacs is configured to evaluate them, or if the user approves the relevant prompt by mistake.</p>



<p>This article outlines configurations for securing file-local and directory-local variables while reducing prompts.</p>



<h2>Allowing safe local variables</h2>



<p>The <code>enable-local-variables</code> variable controls how Emacs handles file-local variables. File-local variables are configurations embedded directly within a file. Here is an example of a file-local variables:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Local variables:</span>
<span class="hljs-comment">;; fill-column: 100</span>
<span class="hljs-comment">;; byte-compile-warnings: (not free-vars)</span>
<span class="hljs-comment">;; End:</span></code></span></pre>


<p>To mitigate security risks and prevent interactive prompts, set <code>enable-local-variables</code> to <code>:safe</code> by adding the following to your init file:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> enable-local-variables <span class="hljs-symbol">:safe</span>)</code></span></pre>


<p>The benefit is that Emacs applies only verified safe values while silently dropping risky ones, reducing prompt fatigue without requiring manual confirmation. The tradeoff is that non-standard but safe project variables will be ignored unless their values are explicitly recognized as safe, for example through <code>safe-local-variable-values</code> or the <code>safe-local-variable</code> property.</p>



<p>Note: Setting <code>enable-local-variables</code> to <code>nil</code> disables normal processing of local variables entirely. (Specific settings, such as <code>lexical-binding</code>, remain active via the <code>permanently-enabled-local-variables</code> list.)</p>



<h2>Whitelisting specific local variables</h2>



<p>When you set <code>enable-local-variables</code> to <code>:safe</code>, Emacs silently ignores unverified variables. To prevent custom project variables from being ignored, you can explicitly mark specific variable/value pairs as safe using <code>safe-local-variable-values</code>, or define a predicate with the <code>safe-local-variable</code> property:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Whitelist a specific variable and value: Add the pair directly to the list.</span>
(<span class="hljs-name">add-to-list</span> 'safe-local-variable-values '(my-custom-variable . <span class="hljs-string">"expected-value"</span>))

<span class="hljs-comment">;; Whitelist a variable based on a predicate type: Define a property that</span>
<span class="hljs-comment">;; validates the variable type to accept any matching value.</span>
(<span class="hljs-name">put</span> 'my-custom-variable 'safe-local-variable #'stringp)

<span class="hljs-comment">;; Whitelist a variable with a predicate: Only allow "hello" or "world"</span>
<span class="hljs-comment">;; as safe values.</span>
(<span class="hljs-name">put</span> 'my-custom-variable 'safe-local-variable
     (<span class="hljs-name">lambda</span> (<span class="hljs-name">val</span>)
       <span class="hljs-string">"Return t if VAL is 'hello' or 'world'."</span>
       (<span class="hljs-name">member</span> val '(<span class="hljs-string">"hello"</span> <span class="hljs-string">"world"</span>))))</code></span></pre>


<h2>Whitelisting specific directories</h2>



<p>The <code>safe-local-variable-directories</code> variable allows you to designate specific paths where all directory-local variables are automatically considered safe. When a directory is added to this list, Emacs trusts every variable defined in its <code>.dir-locals.el</code> file without prompting. Note that this setting applies exclusively to directory-local variables and completely ignores file-local variables.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">add-to-list</span> 'safe-local-variable-directories <span class="hljs-string">"/path/to/trusted/project/"</span>)</code></span></pre>


<h2>Disabling Local Eval Forms</h2>



<p>The <code>enable-local-eval</code> variable controls whether Emacs processes eval forms in file-local and directory-local variable definitions. Unlike ordinary local variable assignments, <code>eval</code> can cause Emacs to evaluate arbitrary Lisp code when processing the local variables.</p>



<p>If your workflow does not require dynamic project configurations, you should disable this feature by setting this variable to <code>nil</code>:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> enable-local-eval <span class="hljs-literal">nil</span>)</code></span></pre>


<p>The tradeoff is that useful evaluation forms, such as project-specific configurations dynamically generated through <code>eval</code>, will not execute.</p>



<h2>Keeping default values for security</h2>



<p>Emacs comes with the following defaults for local environment handling. Sticking with these defaults is already a good choice:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> enable-dir-local-variables <span class="hljs-literal">t</span>)  <span class="hljs-comment">; Apply directory-local variables.</span>
(<span class="hljs-name">setq</span> enable-remote-dir-locals <span class="hljs-literal">nil</span>)  <span class="hljs-comment">; Prevent loading dir-locals over TRAMP.</span></code></span></pre>


<ul>
<li><strong><code>enable-dir-local-variables</code></strong>: This enables the use of directory-local variables (<code>.dir-locals.el</code>). Note that Emacs also reads <code>.dir-locals-2.el</code> if present, and non-file buffers like Dired can inherit these settings.</li>



<li><strong><code>enable-remote-dir-locals</code></strong>: Leaving this as <code>nil</code> prevents Emacs from loading directory-local variables from remote filesystems. This avoids applying potentially untrusted remote <code>.dir-locals.el</code> settings and avoids the additional work required to search for them.</li>
</ul>
<div class="yarpp yarpp-related yarpp-related-rss yarpp-template-list">

<h3>Related posts:</h3><ol>
<li><a href="https://www.jamescherti.com/minimal-emacs-d/">minimal-emacs.d - A Customizable Emacs init.el and early-init.el for Better Defaults and Optimized Startup</a></li>
<li><a href="https://www.jamescherti.com/emacs-persist-restore-text-scale/">persist-text-scale.el - Persist and Restore the Text Scale</a></li>
<li><a href="https://www.jamescherti.com/emacs-evil-mode-restore-line-column-mark/">Emacs Evil Mode: How to restore both the line and column number of a mark, not just the line number</a></li>
<li><a href="https://www.jamescherti.com/emacs-security-settings/">The Emacs security settings that might silently compromise your system</a></li>
<li><a href="https://www.jamescherti.com/git-smartmv-tool-decide-git-mv-or-mv/">A Git Tool that can decide whether to use 'git mv' or 'mv' to move files and/or directories</a></li>
<li><a href="https://www.jamescherti.com/emacs-compile-angel-byte-native-compile/">The compile-angel Emacs package: Byte-compile and Native-compile Emacs Lisp libraries Automatically</a></li>
<li><a href="https://www.jamescherti.com/emacs-ultisnips-mode-edit-snippets-files/">ultisnips-mode.el - An Emacs major mode for editing Ultisnips snippet files (*.snippets files)</a></li>
</ol>
</div>
</body></html>]]></content>
        <author>
            <name>James Cherti</name>
            <uri>https://www.jamescherti.com</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Curtis McHale: The Search for Knowledge - Emacs Carnival]]></title>
        <id>https://curtismchale.ca/2026/08/31/the-search-for-knowledge-emacs-carnival</id>
        <link href="https://curtismchale.ca/2026/08/31/the-search-for-knowledge-emacs-carnival"/>
        <updated>2026-08-31T16:34:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>August <a href="https://www.chiply.dev/post-august-emacs-carnival">Emacs carnival is about the search for knowledge</a>. While I take notes and link them, I don't use Emacs. I've been on the <a href="https://obsidian.md/">Obsidian</a> train pretty much since it came out with a <a href="https://curtismchale.ca/2021/03/22/why-i-moved-from-obsidian-to-craft/">short deviation into Craft</a>.</p>
<p>I've loved the plugins that Obsidian has, but those plugins are also it's downfall. I used the Long Form plugin for a while, but then <a href="https://curtismchale.ca/2026/03/24/goodbye-longform-hello-emacs">stopped and move that writing to Emacs</a> because the plugin was loosing my data any time I tried to use it. But despite my love for Emacs, I haven't moved my notes over to it and I have no plans on moving my notes over to Emacs.</p>
<p>Currently Obsidian has around 9,000 individual files across my notes and writing. It's saved in a portable markdown format. Obsidian's sync service works flawlessly all the time and the mobile apps are good.</p>
<p>On the Emacs side, every few weeks I have some issue with <a href="https://syncthing.net/">Syncthing</a> that means I need to sort out some type of sync conflict and because of this I really only add content from my phone via <a href="https://www.beorgapp.com/">Beorg</a> to my <code>inbox.org</code> file. I don't edit other files if I can help it, the risk is simply to high that I'll have some huge mess to sort out when I get back to my desktop.</p>
<p>The main reason I don't switch is that <a href="https://curtismchale.ca/2016/09/29/shouldnt-change-productivity-systems/">I don't</a> think that <a href="https://curtismchale.ca/2024/08/31/dont-change-tools-shitposting-ai-and-bad-phones-3-threads/">migrating</a> <a href="https://curtismchale.ca/2023/07/22/new-isnt-better-and-youre-not-the-exception/">tools</a> is the <a href="https://curtismchale.ca/2014/12/30/the-danger-in-change/">big</a> <a href="https://curtismchale.ca/2026/03/07/optimising-for-the-wrong-thing">productivity</a> boost that most people think it is. Yes the Emacs vim key bindings are better than what Obsidian provides. Yes I can very easily hack Emacs to do whatever I want, but Obsidian does everything I need already. So moving my notes to Emacs would most likely be me finding Emacs more fun right now than Obsidian because it's new.</p>
<p>I've wondered about <a href="https://github.com/licht1stein/obsidian.el">accessing Obsidian from Emacs</a> so I have Emacs goodness in my Obisidian, but even that feels like me searching for a reason to use Emacs where I don't really need it.</p>
<p>If you want to read how I setup Obsidian, <a href="https://curtismchale.ca/2025/12/29/my-2026-obsidian-setup">my 2026 walkthrough is still how I do it</a>.</p>
</body></html>]]></content>
        <author>
            <name>Curtis McHale</name>
            <uri>https://curtismchale.ca/blog/tags/emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Irreal: Maiorana On Directory Local Variables]]></title>
        <id>https://irreal.org/blog/?p=14048</id>
        <link href="https://irreal.org/blog/?p=14048"/>
        <updated>2026-08-30T14:37:17.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
Chris Maiorana has a <a href="https://chrismaiorana.com/dont-go-bankrupt-go-local/">useful post on directory local variables</a>. The idea is that rather than putting a lot of context specific configuration in your <code>init.el</code> file, you can put those configurations in a <code>.dir-locals.el</code> file and they will be set only when you open a file in that directory.
</p>
<p>
I really like the idea of directory local variables but it has never worked well for me. That’s mainly because I usually wanted to execute some code using the <code>eval</code> keyword. That always causes some problems involving a stack overflow, no matter how simple the code is. Lately, I’ve been using them a lot in conjunction with <a href="https://github.com/minad/jinx">Jinx</a>, which has the nice feature of adding words to the local file or local directory in addition to the other usual options.
</p>
<p>
I find that really useful for my blogging. I can add post specific words to the post source file itself or I can add them to all posts in my blog directory. Maiorana uses then to set things like compilation settings, fill and wrap settings, and indentation styles.
</p>
<p>
If you have different workflows with different setting needs, directory local variables is a very nice way of dealing with them. Of course, not everything should go in a directory local variable. Some things apply to all your buffers and should go in your <code>init.el</code> but for those things that are specific to particular workflows, directory local variables are just what you need.</p>
</body></html>]]></content>
        <author>
            <name>Irreal</name>
            <uri>https://irreal.org/blog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[James Cherti: Fixing Emacs Dired defaults - Settings for better file management]]></title>
        <id>https://www.jamescherti.com/emacs-dired-configuration/</id>
        <link href="https://www.jamescherti.com/emacs-dired-configuration/"/>
        <updated>2026-08-29T15:45:17.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>This article presents a set of <strong>Emacs Dired</strong> configuration changes to enhance file management. The configurations sort directories before files for easier navigation, hide dotfiles, reduce unnecessary confirmation prompts, prevent UI lockups caused by network latency, among other improvements. Each configuration explains the behavior of the relevant setting, its benefits, and its tradeoffs, making it easier to determine which changes are appropriate.</p>



<h2>Keeping Dired clean by hiding dotfiles</h2>



<p>Using <code>dired-omit-mode</code> helps hide hidden files and directories, such as <code>.git</code> or <code>.DS_Store</code>, by applying a specific regular expression. The tradeoff is that required configuration files are hidden by default, forcing you to toggle <code>dired-omit-mode</code> off to see them. Disabling <code>dired-omit-verbose</code> prevents the mode from displaying status messages when omitting files.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> dired-omit-verbose <span class="hljs-literal">nil</span>)
(<span class="hljs-name">setq</span> dired-omit-files (<span class="hljs-name">concat</span> <span class="hljs-string">"\\`[.]\\'"</span>
                               <span class="hljs-string">"\\|\\.\\(?:elc\\|a\\|o\\|pyc\\|pyo\\|swp\\|class\\)\\'"</span>
                               <span class="hljs-string">"\\|^\\.DS_Store\\'"</span>
                               <span class="hljs-string">"\\|^\\.\\(?:svn\\|git\\)\\'"</span>
                               <span class="hljs-string">"\\|^flycheck_.*"</span>
                               <span class="hljs-string">"\\|^flymake_.*"</span>))

(<span class="hljs-name">add-hook</span> 'dired-mode-hook #'dired-omit-mode)</code></span></pre>


<p>To hide all hidden files whose names begin with a dot, add the following regular expression to the <code>dired-omit-files</code> variable:</p>


<pre><span><code class="hljs language-javascript">(setq dired-omit-files (concat dired-omit-files <span class="hljs-string">"\\|^\\."</span>))</code></span></pre>


<h2>Hiding details</h2>



<p>Enabling <code>dired-hide-details-mode</code> automatically hides file details such as permissions, size, and modification dates.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">add-hook</span> 'dired-mode-hook #'dired-hide-details-mode)</code></span></pre>


<h2>Sorting directories first</h2>



<p>The following code snippet configures Dired to sort directories before files. The benefit is improved navigation, as folders are grouped at the top of the buffer, matching standard file managers. The tradeoff is a deviation from strict alphabetical sorting, which can disorient users accustomed to raw UNIX or Linux <code>ls</code> output.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> ls-lisp-verbosity <span class="hljs-literal">nil</span>
      ls-lisp-dirs-first <span class="hljs-literal">t</span>)

(<span class="hljs-name">when</span> (<span class="hljs-name">eq</span> system-type 'darwin)
  (<span class="hljs-name">setq</span> dired-use-ls-dired <span class="hljs-literal">nil</span>)) <span class="hljs-comment">; macOS/BSD ls</span>

(<span class="hljs-name">let</span> ((<span class="hljs-name">args</span> <span class="hljs-string">"--group-directories-first -ahlv"</span>))
  (<span class="hljs-name">when</span> (<span class="hljs-name">or</span> (<span class="hljs-name">eq</span> system-type 'darwin) (<span class="hljs-name">eq</span> system-type 'berkeley-unix))
    (<span class="hljs-name">if-let*</span> ((<span class="hljs-name">gls</span> (<span class="hljs-name">executable-find</span> <span class="hljs-string">"gls"</span>)))
        (<span class="hljs-name">setq</span> insert-directory-program gls)
      (<span class="hljs-name">setq</span> args <span class="hljs-literal">nil</span>)))
  (<span class="hljs-name">when</span> args
    (<span class="hljs-name">setq</span> dired-listing-switches args)))</code></span></pre>


<h2>Killing the current Dired buffer upon navigating into a different directory</h2>



<p>Setting <code>dired-kill-when-opening-new-dired-buffer</code> to <code>t</code> (Emacs &gt;= 28.1) causes Dired to automatically kill the current Dired buffer upon navigating into a different directory. The benefit is a cleaner buffer list, as it prevents leaving behind a trail of open buffers when traversing deep directory hierarchies. The tradeoff is the loss of buffer state. Navigating back to a parent directory requires generating a new buffer, which erases previous cursor positions, active file marks, and modified subdirectory visibility.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> dired-kill-when-opening-new-dired-buffer <span class="hljs-literal">t</span>)</code></span></pre>


<h2>Version control integration</h2>



<p>Enabling <code>dired-vc-rename-file</code> causes Dired to perform file renames through the underlying version control system when supported, using the <code>vc-rename-file</code> function. The benefit is accurate version control history during Dired rename operations. The tradeoff is that VC renames can introduce slight latency on very large repositories.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> dired-vc-rename-file <span class="hljs-literal">t</span>)</code></span></pre>


<h2>Simplifying deletion confirmations</h2>



<p>This configuration accelerates file cleanup by reducing the number of manual prompts required to delete files and directories.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> dired-deletion-confirmer 'y-or-n-p
      dired-recursive-deletes 'top
      dired-clean-confirm-killing-deleted-buffers <span class="hljs-literal">nil</span>)</code></span></pre>


<ul>
<li>Setting <code>dired-deletion-confirmer</code> to <code>'y-or-n-p</code>: Changes the deletion prompt to accept a single 'y' or 'n' keypress instead of requiring you to type the full word 'yes'.</li>



<li>Setting <code>dired-recursive-deletes</code> to <code>'top</code>: By default, deleting a non-empty directory in Dired can be a tedious process because Emacs will prompt you to confirm the deletion of every single nested subdirectory as it traverses down the tree. Setting this variable to <code>'top</code> establishes a practical balance between speed and safety. Emacs will ask for your confirmation exactly once for the top-level directory you have marked. Once you confirm that initial prompt, it silently and recursively deletes all nested files and folders inside that tree without any further interruptions.</li>



<li>Setting <code>dired-clean-confirm-killing-deleted-buffers</code> to <code>nil</code>: Suppresses the prompt that asks for permission to close Dired buffers associated with directories you just deleted, closing them automatically instead. This makes sense because once a directory is removed from the disk, any open buffer pointing to it becomes orphaned and unusable. Automatically killing these dead buffers keeps your buffer list clean, as deleting the directory already establishes your intent to remove it from your workflow entirely.</li>
</ul>



<p>The benefit is faster file management and fewer repetitive keystrokes. The tradeoff is an elevated risk of accidental data loss, as the safety barrier to executing destructive operations is significantly lowered.</p>



<h2>Removing disk space indicator</h2>



<p>Disabling <code>dired-free-space</code> (Emacs &gt;= 29.1) removes disk space indicator from Dired buffers. The tradeoff is that you lose quick visibility into remaining disk capacity.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> dired-free-space <span class="hljs-literal">nil</span>)</code></span></pre>


<h2>Restricting vertical cursor movement</h2>



<p>Setting <code>dired-movement-style</code> to <code>'bounded-files</code> (Emacs &gt;= 29.1) restricts vertical movement to file lines in the Dired buffer. When point reaches the first or last file line, further vertical movement stops instead of moving to other non-file lines. The benefit is increased navigation efficiency. When scrolling rapidly to the top or bottom of a directory, the cursor stops exactly on the first or last file or directory, avoiding empty space or metadata lines. The tradeoff is that placing the cursor on the directory header to copy its path requires alternative navigation commands, as standard vertical movement keys will no longer reach it.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> dired-movement-style 'bounded-files)</code></span></pre>


<h2>Managing recursive copies and destination directories</h2>



<p>These variables force Dired to copy directories recursively by default without prompting, while explicitly asking for confirmation before creating new, non-existent destination directories. The benefit is bulk copying combined with a safety check against typos in destination paths. The tradeoff is that large, deeply nested directory structures might be copied unintentionally since the recursive copying happens automatically.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> dired-recursive-copies 'always
      dired-create-destination-dirs 'ask)</code></span></pre>


<h2>Efficient auto-reverting for dired buffers</h2>



<p>Configuring <code>dired-auto-revert-buffer</code> to use <code>dired-directory-changed-p</code> causes Dired to automatically revert the buffer when the listed directory has changed. The benefit is improved efficiency because Dired does not unconditionally rebuild the listing. The tradeoff is that changes that do not modify the directory itself, such as in-place edits to existing files, might not trigger an automatic refresh.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> dired-auto-revert-buffer 'dired-directory-changed-p)</code></span></pre>


<p>Note: On Unix-like filesystems, a directory's mtime (modification time) is updated when its directory entries change. This occurs when a file or subdirectory is created, deleted, or renamed within the directory. Modifying the data inside an existing file updates the file's mtime, but does not update the mtime of its parent directory.</p>



<h2>Reverting destination buffers after file operations</h2>



<p>This configuration automatically updates destination Dired buffers after file operations like copying or renaming, but uses a custom predicate to skip remote directories. The benefit is automatic synchronization of local Dired buffers without performing the same operation on remote directories, avoiding unnecessary TRAMP network activity and associated latency. The tradeoff is that remote directory buffers require a manual refresh to reflect recent file transfers.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> dired-do-revert-buffer (<span class="hljs-name">lambda</span> (<span class="hljs-name">dir</span>)
                               (<span class="hljs-name">not</span> (<span class="hljs-name">file-remote-p</span> dir))))</code></span></pre>


<h2>Enabling mouse drag-and-drop</h2>



<p>Setting <code>dired-mouse-drag-files</code> to <code>t</code> (Emacs &gt;= 29.1) allows you to click and drag files directly from a Dired buffer into external desktop applications, such as graphical file managers or web browsers.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> dired-mouse-drag-files <span class="hljs-literal">t</span>)</code></span></pre>


<h2>Cross-platform file associations</h2>



<p>The following snippet configures Dired to open specific file extensions using the default application handler of the host operating system. It identifies the environment (macOS, Linux, or Windows) and assigns the corresponding system command (<code>open</code>, <code>xdg-open</code>, or <code>start</code>) to the <code>dired-guess-shell-alist-user</code> variable for documents, images, and media files. The main benefit is that it delegates file association management to the operating system, removing the need to configure individual applications within Emacs for every file type.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">defvar</span> my-dired-xdg-open-cmd <span class="hljs-literal">nil</span>)
(<span class="hljs-name">with-eval-after-load</span> 'dired
  (<span class="hljs-name">when-let*</span> ((<span class="hljs-name">cmd</span> (<span class="hljs-name">cond</span>
                    ((<span class="hljs-name">eq</span> system-type 'darwin)
                     <span class="hljs-string">"open"</span>)
                    ((<span class="hljs-name">memq</span> system-type '(gnu gnu/linux gnu/kfreebsd
                                             berkeley-unix))
                     <span class="hljs-string">"xdg-open"</span>)
                    ((<span class="hljs-name">memq</span> system-type '(cygwin windows-nt ms-dos))
                     <span class="hljs-string">"start"</span>))))
    (<span class="hljs-name">setq</span> dired-guess-shell-alist-user
          `((<span class="hljs-string">"\\.\\(?:docx\\|pdf\\|odt\\|odg\\|ods\\|djvu\\|eps\\)\\'"</span> ,cmd)
            (<span class="hljs-string">"\\.\\(?:jpe?g\\|webp\\|png\\|gif\\|xpm\\)\\'"</span> ,cmd)
            (<span class="hljs-string">"\\.\\(?:xcf\\)\\'"</span> ,cmd)
            (<span class="hljs-string">"\\.tex\\'"</span> ,cmd)
            (<span class="hljs-string">"\\.\\(?:mp4\\|mkv\\|m4a\\|avi\\|flv\\|rm\\|rmvb\\|ogv\\)\\(?:\\.part\\)?\\'"</span> ,cmd)
            (<span class="hljs-string">"\\.\\(?:mp3\\|flac\\)\\'"</span> ,cmd)))
    (<span class="hljs-name">when</span> cmd
      (<span class="hljs-name">setq</span> my-dired-xdg-open-cmd cmd))))</code></span></pre>


<p><strong>How to use it: </strong>To use this configuration in practice, navigate to a directory using Dired and place your cursor over a file. For example, if your cursor is on a video file, press <code>!</code> (which invokes <code>dired-do-shell-command</code>) or <code>&amp;</code> (for asynchronous execution). Dired will prompt you in the minibuffer with the appropriate system command already populated based on your operating system, such as <code>xdg-open</code>. Press <code>RET</code> to confirm, and the video will open in your operating system's default media player.</p>



<p>If you prefer forcing all file types to open asynchronously via the operating system handler (e.g., <code>xdg-open</code>), the following snippet routes every file through the system default application.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">defvar</span> my-dired-xdg-open-cmd <span class="hljs-literal">nil</span>)
(<span class="hljs-name">with-eval-after-load</span> 'dired
  (<span class="hljs-name">when-let*</span> ((<span class="hljs-name">cmd</span> (<span class="hljs-name">cond</span>
                    ((<span class="hljs-name">eq</span> system-type 'darwin)
                     <span class="hljs-string">"open"</span>)
                    ((<span class="hljs-name">memq</span> system-type '(gnu gnu/linux gnu/kfreebsd
                                             berkeley-unix))
                     <span class="hljs-string">"xdg-open"</span>)
                    ((<span class="hljs-name">memq</span> system-type '(cygwin windows-nt ms-dos))
                     <span class="hljs-string">"start"</span>))))
    (<span class="hljs-name">setq</span> dired-guess-shell-alist-user
          `((<span class="hljs-string">".*"</span> ,cmd)))
    (<span class="hljs-name">when</span> cmd
      (<span class="hljs-name">setq</span> my-dired-xdg-open-cmd cmd))))</code></span></pre><div class="yarpp yarpp-related yarpp-related-rss yarpp-template-list">

<h3>Related posts:</h3><ol>
<li><a href="https://www.jamescherti.com/fold-outline-indentation-emacs-package/">outline-indent.el - A modern indentation-based folding mode for Emacs</a></li>
<li><a href="https://www.jamescherti.com/emacs-persist-restore-text-scale/">persist-text-scale.el - Persist and Restore the Text Scale</a></li>
<li><a href="https://www.jamescherti.com/easysession-el-persist-restore-emacs-session/">easysession.el: Easily persist and restore Emacs sessions (windows, tab-bar, file buffers, scratch, Dired, narrowing, indirect buffers/clones, Magit buffers...); a robust desktop.el replacement</a></li>
<li><a href="https://www.jamescherti.com/emacs-the-definitive-guide-to-code-folding/">The Definitive Guide to Code Folding in Emacs</a></li>
<li><a href="https://www.jamescherti.com/emacs-native-compilation-config-jobs/">Enabling Emacs Native Compilation and Dynamically Adjusting the Number of Elisp Files Compiled in Parallel</a></li>
<li><a href="https://www.jamescherti.com/securing-emacs-dir-locals-el-local-variables/">Securing Emacs .dir-locals.el and local variables</a></li>
<li><a href="https://www.jamescherti.com/emacs-security-settings/">The Emacs security settings that might silently compromise your system</a></li>
</ol>
</div>
</body></html>]]></content>
        <author>
            <name>James Cherti</name>
            <uri>https://www.jamescherti.com</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Irreal: Emacs Configuration Organization]]></title>
        <id>https://irreal.org/blog/?p=14046</id>
        <link href="https://irreal.org/blog/?p=14046"/>
        <updated>2026-08-29T15:12:53.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
Emacs configurations are like <a href="https://irreal.org/blog/?p=8476">well shuffled card decks</a>: no two are alike. But, as Arlo Guthrie famously sang, that’s <a href="https://en.wikipedia.org/wiki/Alice%27s_Restaurant#Part_Two">“not what I came to tell you about”</a>. Rather, this post is about differing organizations for those Emacs configurations.
</p>
<p>
The idea was suggested to me by <a href="https://en.andros.dev/blog/dc7db35a/how-i-organize-my-emacs-configuration/">a post from Andros Fenollosa</a> on his own Emacs configuration. Fenollosa says that he doesn’t use any of the all-in-one configurations like Spacemacs or Doom because they include too much and are hard to understand and make changes to. He also doesn’t use the popular literate configuration method using Org mode because it’s just another layer of complexity that can go wrong.
</p>
<p>
His idea is to to have a separate file for each “area of responsibility” and to use <code>init.el</code> as a sort of index that loads them in order. I like that idea but don’t use separate files. I keep everything in <code>init.el</code> and just organize it in sections.
</p>
<p>
Still, Fenollosa has a good argument for the separate files but I think I get the same benefits with a single file broken into sections. Actually, it’s not quite true that everything is in a single file. I run Emacs on several type of systems and keep the system/OS specific information in separate files that I load conditionally based on the system and OS type<sup><a href="https://irreal.org/blog#fn.1">1</a></sup>. That way I don’t have a lot of conditionals dealing with system/OS type in <code>init.el</code> itself.
</p>
<p>
There aren’t as many ways of organizing your Emacs configuration as there are configurations but there are a lot of them. What principle do you use?
</p>
<div>
<h2>Footnotes: </h2>
<div>
<div class="footdef"><sup><a href="https://irreal.org/blog#fnr.1">1</a></sup> <p></p>
<div class="footpara">
<p>
If you’re interested, here’s the code:
</p>
<div class="org-src-container">
<pre><code><span>;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;</span><span>
</span><span>;; </span><span>Pull in system and platform specific configurations                    ;;
</span><span>;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;</span><span>
</span>
<span>;; </span><span>Just keep on going if the requisite file isn't there.
</span><span>;; </span><span>Manipulations in second load is for "gnu/linux" → "linux"
</span>
(load (car (split-string (system-name) <span>"\\."</span>)) t)
(load (car (reverse (split-string (symbol-name system-type) <span>"/"</span>))) t)
</code></pre>
</div>
</div>
</div>
</div>
</div>
</body></html>]]></content>
        <author>
            <name>Irreal</name>
            <uri>https://irreal.org/blog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Sacha Chua: Carnaval d'Emacs d'août 2026 : la gestion de l'information et les graphes de connaissances]]></title>
        <id>https://sachachua.com/blog/2026/08/carnaval-d-emacs-d-aout-la-gestion-d-information-et-les-graphes-de-connaissances/</id>
        <link href="https://sachachua.com/blog/2026/08/carnaval-d-emacs-d-aout-la-gestion-d-information-et-les-graphes-de-connaissances/"/>
        <updated>2026-08-29T14:52:30.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><div class="update">
<p>
English: <a href="https://sachachua.com/blog/2026/08/emacs-carnival-aug-2026-information-management-and-knowledge-graphs/">Emacs Carnival Aug 2026: Information management and knowledge graphs</a>
</p>

</div>

<p>
Cet article est inspiré par le <a href="https://www.emacswiki.org/emacs/Carnival">Carnaval d'Emacs</a> sur <a href="https://www.chiply.dev/post-august-emacs-carnival">la recherche de connaissances</a>. Merci à Charlie Holland pour son accueil ! C'est une bonne occasion de réfléchir à la gestion de mes notes, qui est un de mes intérêts forts.
</p>

<p>
<b>Saisir :</b> Je souhaite saisir et publier ce que j'apprends aussitôt que possible parce que ma mémoire n'est pas fiable. Je préfère le format texte brut car il est le plus consultable et le plus archivable à long terme. Je suis loin de mon ordinateur la plupart du temps, donc j'utilise <a href="https://www.orgzlyrevived.com/">Orgzly Revived</a> sur mon téléphone pour saisir des notes courtes dans ma boîte de réception inbox.org ou mon fichier de brouillons posts.org. Une fois que je suis sur mon ordinateur, je développe mes notes en utilisant <a href="https://orgmode.org">Org Mode</a> sur <a href="https://www.gnu.org/savannah-checkouts/gnu/emacs/emacs.html">Emacs</a>. J'utilise <code>org-refile</code> pour déplacer des notes vers d'autres fichiers comme organizer.org. J'utilise <a href="https://sachachua.com/dotemacs#org-files">quelques grands fichiers Org</a>. Pour les tâches répétitives comme <a href="https://sachachua.com/topic/workflows/">mes flux de travail</a>, j'y ajoute des détails autant que possible.
</p>

<p>
<b>Chercher</b> : Pour chercher mes notes publiques, j'utilise souvent Google. Les notes publiques me permettent de les récupérer facilement. Si je publie mes notes, d'autres personnes peuvent en profiter et les enrichir. Forcément, j'ai aussi des notes personnelles. J'utilise <a href="https://orgmode.org/manual/Refile-and-Copy.html">org-refile</a> pour chercher par titres ou <a href="https://github.com/minad/consult">consult-ripgrep</a> pour naviguer dans mes notes privées. J'utilise aussi <code>consult-line</code> et <code>isearch</code> si je veux chercher par mots dans le corps. Ils reposent sur des comparaisons exactes, mais si j'essaie des mots similaires, je ne peux pas trouver ce que je cherche. C'est la raison pour laquelle je m'intéresse au paquet <a href="https://github.com/zkry/p-search">p-search</a> et <a href="https://sachachua.com/dotemacs/index.html#org-mode-vector-search">aux plongements de phrases</a> (embeddings) pour la recherche approximative, mais je n'ai pas encore mis en place un bon flux de travail. Je rêve d'un système pour suggérer automatiquement des liens vers mes autres articles, ma configuration et mes notes privées, ce qui peut me rafraîchir la mémoire sur des choses oubliées. Une vraie évaluation doit attendre d'avoir plus de temps libre.
</p>

<p>
<b>Naviguer</b> : J'utilise <code>C-u org-refile</code> pour naviguer dans mes sous-titres n'importe où dans mes fichiers <code>org-refile-targets</code>. Je relis aussi ma boîte de réception et mes brouillons de temps en temps. J'ai une fonction <a href="https://sachachua.com/dotemacs#embark-11ty">sacha-blog-edit-org</a> qui ouvre le code source Org à partir d'un lien, même s'il est dans mon posts.org ou seulement dans la copie publiée.
</p>

<p>
<b>Lier</b> : Beaucoup de mes idées sont inspirées par des articles d'autres personnes, donc quand je trouve un article intéressant sur mon téléphone, je l'envoie à Orgzly Revived pour l'ajouter à ma boîte de réception. Ça me permet d'inclure le lien ou les liens dans l'article une fois que je réussis finalement à l'écrire et à le publier.
</p>

<p>
J'ai une petite fonction <a href="https://sachachua.com/dotemacs#mastodon-mastodon-el-mention-people-based-on-regexp">sacha-org-contacts-suggest-mentions</a> qui m'aide à notifier l'auteur de l'article précédent et peut-être d'autres personnes qui sont potentiellement intéressées en utilisant mon fichier people.org, où j'ai spécifié des expressions régulières à comparer au texte.
</p>

<p>
Pour m'aider à lier l'article aux autres ressources, j'ai des fonctions pour lier :
</p>
<ul>
<li><a href="https://sachachua.com/dotemacs#linking-to-blog-posts">aux articles sur mon blog</a> avec le genre de lien "blog"</li>
<li><a href="https://sachachua.com/dotemacs#completion-consult-consult-omni-using-web-searches-and-bookmarks-to-quickly-link-placeholders-in-org-mode">aux autres sites automatiquement à partir du texte du lien</a></li>
</ul>
<p>
(Hmm, je peux automatiser les liens vers les autres parties de ma configuration qui définissent les autres fonctions…)
</p>

<p>
Mes pensées sont souvent déconnectées à cause d'un cerveau qui aime sauter d'un sujet à l'autre.​ (Bien évidemment, même avec cet article…) En écrivant un article, j'ajoute de nombreuses idées dans ma boîte de réception. À bien y penser, je pourrais ajouter à Org Mode un genre de lien qui résoudra un vrai lien une fois que l'article lié sera publié, comme les WikiWords, ce qui peut m'aider à les connecter. Je sais qu'il y a des paquets qui offrent cette fonctionnalité.
</p>

<p>
<b>Publier</b> : J'utilise le générateur de site statique <a href="https://www.11ty.dev/">11ty</a> avec <a href="https://github.com/sachac/ox-11ty/blob/master/ox-11ty.el">ox-11ty.el</a>. Une fois que je publie une note, <a href="https://sachachua.com/dotemacs#moving-sacha-org-post-subtree-to-the-11ty-directory">le code source org est aussi copié dans le même répertoire</a>.
</p>
<div class="outline-3">
<h3><a href="https://sachachua.com/blog/feed/index.xml#carnaval-d-emacs-d-ao-t-2026-la-gestion-d-information-et-les-graphes-de-connaissances-visualisation-et-exploration">Visualisation et exploration</a></h3>
<div class="outline-text-3">
<p>
J'ai toujours envie de graphes de connaissances comme celui dans <a href="https://www.jerrysbrain.com/">le cerveau de Jerry</a>. Je pense que l'affichage du voisinage autour de l'article actuel est plus utile qu'un aperçu global (comme ceux de ) qui est impressionnant mais un peu trop difficile à utiliser.​
</p>

<p>
J'adore également les grands jardins publics de connaissances comme celui d'<a href="https://andymatuschak.org/">Andy Matuschak</a> même si on n'a pas une véritable carte ou un graphe. La richesse des liens m'encourage à les explorer. Je préfère les liens intégrés plutôt que les listes ou les connexions ​sans explications. Je suis curieuse aussi du projet <a href="https://anagora.org">Anagora</a>, qui essaye de créer un grand réseau de graphes de connaissances personnels et de faciliter le saut de l'un à l'autre par sujet.
</p>

<p>
Mais je ne veux pas investir trop d'efforts pour le faire moi-même. La majorité de mes articles (sauf le bulletin d'information <a href="https://sachachua.com/blog/category/emacs-news">Emacs News</a> et les revues) contiennent peu de liens. Si je reste au niveau des catégories, il y a trop de points près d'Emacs. Je pourrais convertir les sous-catégories dans <a href="https://sachachua.com/topic/">mon catalogue</a> en données pour la visualisation… Quand même, le catalogue aurait bien besoin d'une mise à jour.
</p>

<p>
Il y a d'autres genres de graphes que j'utilise fréquemment. <a href="https://sachachua.com/blog/2025/07/finding-the-shape-of-my-thoughts/">Je dessine souvent pendant que j'écris</a>. Je fais des cartes mentales et des notes dessinées. En fait, c'est plutôt que je jette plusieurs mots sur une page et puis que je les déplace, les connecte, et les range graduellement. Je le fais pour découvrir ce que je veux dire et comment je peux les organiser. De temps en temps, j'inclus des dessins dans les articles ou les publie dans <a href="https://sketches.sachachua.com">mon carnet de croquis public</a>, et c'est une agréable surprise quand un de ces dessins intéresse d'autres personnes.
</p>

<p>
J'accumule beaucoup de brouillons dans mon fichier posts.org, que je synchronise avec mon téléphone via <a href="https://syncthing.net/">Syncthing</a> pour éditer sur Orgzly Revived. De temps en temps, j'utilise <a href="https://sachachua.com/blog/2025/01/treemap-visualization-of-an-org-mode-file/">une carte arborescente</a> (ou treemap) pour visualiser les tailles des brouillons oubliés (et qui sont peut-être presque finalisés, il leur manque seulement une petite révision ou un ajout). (Hmm, je me demande comment je peux ajouter la taille du sous-arbre aux titres d'Org…)
</p>


<figure>
<a href="https://sachachua.com/blog/2026/08/carnaval-d-emacs-d-aout-la-gestion-d-information-et-les-graphes-de-connaissances/webtreemap.html"><img src="https://sachachua.com/blog/2026/08/carnaval-d-emacs-d-aout-la-gestion-d-information-et-les-graphes-de-connaissances/2026-08-29_08-16-41.png" alt="2026-08-29_08-16-41.png"></a>

<figcaption><span class="figure-number">Figure 1: </span>Une capture d'écran de ma carte arborescente pour mon fichier actuel posts.org</figcaption>
</figure>

<p>
En plus, j'apprécie l'analyse des tendances saisonnières ou annuelles dans ma fréquence de publication (qui me rassure sur le fait que les journées chargées de l'été sont normales et je retrouverai bientôt un peu plus de temps libre):
</p>

Tendances mensuelles
<div class="org-src-container">
<pre><code><span class="org-keyword">import</span> json
<span class="org-keyword">import</span> seaborn <span class="org-keyword">as</span> sns
<span class="org-keyword">import</span> re
<span class="org-keyword">from</span> collections <span class="org-keyword">import</span> defaultdict
<span class="org-keyword">import</span> requests
<span class="org-keyword">import</span> numpy <span class="org-keyword">as</span> np
<span class="org-keyword">import</span> pandas <span class="org-keyword">as</span> pd
<span class="org-keyword">import</span> matplotlib.pyplot <span class="org-keyword">as</span> plt
<span class="org-keyword">with</span> <span class="org-builtin">open</span>(<span class="org-string">'/home/sacha/proj/static-blog/_site/blog/all/index.json'</span>) <span class="org-keyword">as</span> jsonfile:
    <span class="org-variable-name">posts</span> <span class="org-operator">=</span> json.load(jsonfile)
    jsonfile.close()
<span class="org-variable-name">monthly_counts</span> <span class="org-operator">=</span> defaultdict(<span class="org-keyword">lambda</span>: defaultdict(<span class="org-builtin">int</span>))
<span class="org-keyword">for</span> post <span class="org-keyword">in</span> posts:
    <span class="org-variable-name">title</span> <span class="org-operator">=</span> post.get(<span class="org-string">"title"</span>, <span class="org-string">""</span>)
    <span class="org-variable-name">date_str</span> <span class="org-operator">=</span> post.get(<span class="org-string">"date"</span>, <span class="org-string">""</span>)
    <span class="org-keyword">if</span> re.search(r<span class="org-string">'emacs news'</span>, title, re.IGNORECASE):
        <span class="org-keyword">continue</span>
    <span class="org-keyword">if</span> date_str <span class="org-keyword">and</span> <span class="org-builtin">len</span>(date_str) <span class="org-operator">&gt;=</span> 7:
        <span class="org-variable-name">year</span> <span class="org-operator">=</span> date_str[:4]
        <span class="org-variable-name">month</span> <span class="org-operator">=</span> <span class="org-builtin">int</span>(date_str[5:7])
        monthly_counts[year][month] <span class="org-operator">+=</span> 1
<span class="org-variable-name">months_labels</span> <span class="org-operator">=</span> [<span class="org-string">'Jan'</span>, <span class="org-string">'Fév'</span>, <span class="org-string">'Mar'</span>, <span class="org-string">'Avr'</span>, <span class="org-string">'Mai'</span>, <span class="org-string">'Juin'</span>, <span class="org-string">'Juil'</span>, <span class="org-string">'Août'</span>, <span class="org-string">'Sept'</span>, <span class="org-string">'Oct'</span>, <span class="org-string">'Nov'</span>, <span class="org-string">'Déc'</span>]
<span class="org-variable-name">years</span> <span class="org-operator">=</span> [<span class="org-string">'2022'</span>, <span class="org-string">'2023'</span>, <span class="org-string">'2024'</span>, <span class="org-string">'2025'</span>, <span class="org-string">'2026'</span>]
<span class="org-variable-name">df</span> <span class="org-operator">=</span> pd.DataFrame(index<span class="org-operator">=</span><span class="org-builtin">range</span>(1, 13), data<span class="org-operator">=</span>{<span class="org-string">'Mois'</span>: months_labels})
<span class="org-keyword">for</span> year <span class="org-keyword">in</span> years:
    <span class="org-variable-name">df</span>[year] <span class="org-operator">=</span> [monthly_counts[year][m] <span class="org-keyword">for</span> m <span class="org-keyword">in</span> <span class="org-builtin">range</span>(1, 13)]
df.<span class="org-variable-name">iloc</span>[8:, <span class="org-operator">-</span>1] <span class="org-operator">=</span> <span class="org-constant">None</span>
<span class="org-variable-name">df_long</span> <span class="org-operator">=</span> df.melt(id_vars<span class="org-operator">=</span>[<span class="org-string">'Mois'</span>], value_vars<span class="org-operator">=</span>years, var_name<span class="org-operator">=</span><span class="org-string">'Année'</span>, value_name<span class="org-operator">=</span><span class="org-string">'Compte'</span>)
<span class="org-variable-name">num_old_years</span> <span class="org-operator">=</span> <span class="org-builtin">len</span>(years) <span class="org-operator">-</span> 1
<span class="org-variable-name">gray_shades</span> <span class="org-operator">=</span> np.linspace(0.8, 0.3, num_old_years)  <span class="org-comment-delimiter"># </span><span class="org-comment">0.8 is lighter, 0.3 is darker</span>
<span class="org-variable-name">custom_colors</span> <span class="org-operator">=</span> {year: <span class="org-builtin">str</span>(shade) <span class="org-keyword">for</span> year, shade <span class="org-keyword">in</span> <span class="org-builtin">zip</span>(years[:<span class="org-operator">-</span>1], gray_shades)}
<span class="org-variable-name">custom_colors</span>[<span class="org-string">'2026'</span>] <span class="org-operator">=</span> <span class="org-string">"#000000"</span>  <span class="org-comment-delimiter"># </span><span class="org-comment">Force the current year to pure black</span>
<span class="org-variable-name">widths</span> <span class="org-operator">=</span> {year: 1 <span class="org-keyword">for</span> year <span class="org-keyword">in</span> years}
<span class="org-variable-name">widths</span>[<span class="org-string">'2026'</span>] <span class="org-operator">=</span> 3
plt.figure(figsize<span class="org-operator">=</span>(10, 5))
<span class="org-variable-name">ax</span> <span class="org-operator">=</span> sns.lineplot(data<span class="org-operator">=</span>df_long, hue<span class="org-operator">=</span><span class="org-string">'Année'</span>, x<span class="org-operator">=</span><span class="org-string">'Mois'</span>, y<span class="org-operator">=</span><span class="org-string">'Compte'</span>, size<span class="org-operator">=</span><span class="org-string">'Année'</span>, palette<span class="org-operator">=</span>custom_colors, sizes<span class="org-operator">=</span>widths, sort<span class="org-operator">=</span><span class="org-constant">False</span>)
ax.set_xticks(<span class="org-builtin">range</span>(12))
ax.set_xticklabels(months_labels)
plt.title(<span class="org-string">"Fréquence mensuelle des articles sur sachachua.com</span><span class="org-constant">\n</span><span class="org-string">(hors Emacs News)"</span>, fontsize<span class="org-operator">=</span>12, fontweight<span class="org-operator">=</span><span class="org-string">'bold'</span>)
plt.xlabel(<span class="org-string">"Mois"</span>, fontsize<span class="org-operator">=</span>10)
plt.ylabel(<span class="org-string">"Nombre d'articles publiés"</span>, fontsize<span class="org-operator">=</span>10)
plt.grid(<span class="org-constant">True</span>, linestyle<span class="org-operator">=</span><span class="org-string">'--'</span>, alpha<span class="org-operator">=</span>0.5)
plt.legend(loc<span class="org-operator">=</span><span class="org-string">'upper right'</span>)
plt.tight_layout()
plt.savefig(<span class="org-string">'frequence.svg'</span>)
<span class="org-keyword">return</span> df
</code></pre>
</div>



<table>


<colgroup>
<col>

<col>

<col>

<col>

<col>

<col>

<col>
</colgroup>
<thead>
<tr>
<th>&nbsp;</th>
<th>Mois</th>
<th>2022</th>
<th>2023</th>
<th>2024</th>
<th>2025</th>
<th>2026</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Jan</td>
<td>6</td>
<td>19</td>
<td>20</td>
<td>23</td>
<td>16.0</td>
</tr>

<tr>
<td>2</td>
<td>Fév</td>
<td>0</td>
<td>3</td>
<td>0</td>
<td>10</td>
<td>8.0</td>
</tr>

<tr>
<td>3</td>
<td>Mar</td>
<td>0</td>
<td>5</td>
<td>1</td>
<td>24</td>
<td>15.0</td>
</tr>

<tr>
<td>4</td>
<td>Avr</td>
<td>0</td>
<td>2</td>
<td>1</td>
<td>14</td>
<td>22.0</td>
</tr>

<tr>
<td>5</td>
<td>Mai</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>9</td>
<td>15.0</td>
</tr>

<tr>
<td>6</td>
<td>Juin</td>
<td>0</td>
<td>3</td>
<td>1</td>
<td>9</td>
<td>13.0</td>
</tr>

<tr>
<td>7</td>
<td>Juil</td>
<td>1</td>
<td>0</td>
<td>0</td>
<td>6</td>
<td>9.0</td>
</tr>

<tr>
<td>8</td>
<td>Août</td>
<td>7</td>
<td>2</td>
<td>1</td>
<td>5</td>
<td>11.0</td>
</tr>

<tr>
<td>9</td>
<td>Sept</td>
<td>1</td>
<td>8</td>
<td>12</td>
<td>17</td>
<td>nan</td>
</tr>

<tr>
<td>10</td>
<td>Oct</td>
<td>2</td>
<td>12</td>
<td>29</td>
<td>11</td>
<td>nan</td>
</tr>

<tr>
<td>11</td>
<td>Nov</td>
<td>5</td>
<td>1</td>
<td>19</td>
<td>8</td>
<td>nan</td>
</tr>

<tr>
<td>12</td>
<td>Déc</td>
<td>4</td>
<td>15</td>
<td>6</td>
<td>6</td>
<td>nan</td>
</tr>
</tbody>
</table>


<figure>



 
  
   
    
    2026-08-30T08:43:07.159204
    image/svg+xml
    
     
      Matplotlib v3.11.0, https://matplotlib.org/
     
    
   
  
 
 
  
 
 
  
   
  
  
   
    
   
   
   
   
   
   
   
    
     
      
     
     
      
       
      
      
       
      
     
     
      
      
       
        
        
        
       
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
        
        
       
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
        
       
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
       
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
       
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
       
       
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
       
       
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
        
        
       
       
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
        
        
       
       
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
        
       
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
       
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
       
       
       
       
      
     
    
    
     
     
      
       
      
      
      
      
      
     
    
   
   
    
     
      
     
     
      
       
      
      
       
      
     
     
      
      
       
        
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
       
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
       
      
     
    
    
     
      
     
     
      
       
      
     
     
      
      
       
        
       
       
       
      
     
    
    
     
     
      
       
       
       
       
       
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
     
    
   
   
    
   
   
    
   
   
    
   
   
    
   
   
    
   
   
   
   
   
   
   
    
   
   
    
   
   
    
   
   
    
   
   
    
    
     
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
    
    
    
     
      
      
      
      
      
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
    
   
   
    
     
    
    
     
    
    
     
     
      
      
      
      
     
    
    
     
    
    
     
     
      
      
      
      
     
    
    
     
    
    
     
     
      
       
      
      
      
      
      
     
    
    
     
    
    
     
     
      
      
      
      
     
    
    
     
    
    
     
     
      
       
      
      
      
      
      
     
    
   
  
 
 
  
   
  
 



</figure>

<p>
et la croissance graduelle de mon vocabulaire français selon mon journal:
</p>


<figure>
<img src="https://sachachua.com/blog/2026/08/carnaval-d-emacs-d-aout-la-gestion-d-information-et-les-graphes-de-connaissances/01_cumulative_vocab.png" alt="01_cumulative_vocab.png">

</figure>

<p>
Bien qu'ils ne soient pas des graphes de connaissances classiques, ils m'aident à voir des tendances qui ne sont pas évidentes au jour le jour.
</p>
</div>
</div>
<div class="outline-3">
<h3><a href="https://sachachua.com/blog/feed/index.xml#carnaval-d-emacs-d-ao-t-2026-la-gestion-d-information-et-les-graphes-de-connaissances-savoir-collectif">Savoir collectif</a></h3>
<div class="outline-text-3">
<p>
Je suis plus intéressée par le réseau de savoir collectif plutôt que par mes notes personnelles. Depuis plus de dix ans, je rassemble et catégorise de nombreux liens pour <a href="https://sachachua.com/blog/category/emacs-news">Emacs News</a>, ce qui permet de les parcourir. J'adore rencontrer beaucoup d'idées et de nombreuses personnes en chemin. C'est facile à faire et cela ne demande pas trop de temps, donc j'ai pu  continuer ainsi malgré les interruptions de ma vie de maman. En parlant de graphes de connaissances, le bulletin d'information sert à connecter des nœuds et des personnes. De temps en temps, je suis très contente d'entendre qu'un article en inspire un autre, et encore un autre, puis une collaboration… C'est le pouvoir de créer des liens entre les personnes qui apprécient des choses similaires. Si je deviens incapable de faire ce bulletin hebdomadaire, j'espère que quelqu'un le continuera.
</p>

<p>
Grâce à mon assemblage de liens pour Emacs News, je rencontre souvent des occasions de recommander un article ou une personne en réponse à une question ou un message. Si je peux trouver l'article dans mon fichier d'archive d'Emacs News avec <a href="https://github.com/minad/consult">consult-line</a> ou <a href="https://emacsredux.com/blog/2025/03/18/you-have-no-idea-how-powerful-isearch-is/">isearch</a>, je peux coller le lien exact. De temps en temps, je veux présenter une personne, ce qui est un peu difficile sur Mastodon parce que les noms de Mastodon sont souvent très différents de leurs vrais noms. Je note les noms de Mastodon dans mon fichier people.org, et j'ai une petite fonction <a href="https://sachachua.com/dotemacs#mastodon-mastodon-el-mention-people-based-on-regexp">sacha-mastodon-insert-handle-from-contacts</a> pour compléter les noms de Mastodon. J'ai aussi une autre fonction <a href="https://sachachua.com/dotemacs#mastodon-mastodon-el-mention-people-based-on-regexp">sacha-mastodon-insert-interested-handles</a> qui ajoute des personnes qui peuvent être intéressées sur la base des correspondances d'expressions régulières dans le message, comme quand j'écris mes articles.
</p>

<p>
En plus des connexions entre des articles et des personnes, je suis également intéressée par les connexions entre des sujets. Avant d'apprendre une chose, quels sujets doivent la précéder ? Quand on apprend une chose, qu'est-ce qui est proche et plus facile à apprendre par la suite ? C'est peut-être utile pour donner des conseils ou des suggestions en tant que coach, ou pour apprendre en mode autodidacte. Les besoins et les chemins diffèrent selon la personne, donc c'est impossible de tracer un parcours qui convient à tout le monde. J'ai commencé à faire <a href="https://sachachua.com/web/beginner-map.html">une carte des ressources pour les débutants</a>, mais je pense que c'est toujours un peu intimidant, même pour moi. La place naturelle de ces liens est peut-être <a href="https://emacswiki.org">EmacsWiki</a> pour permettre aux autres personnes de les trouver et de les enrichir, si je trouve une façon de sauvegarder les données et si je résous le problème de l'édition de quelques pages. En plus, si j'étudie les flux de travail et que je les classifie par aspect, je pourrais aider quelqu'un à trouver des ressources qui sont similaires à ses idées.
</p>

<p>
Je veux aussi identifier des lacunes sur lesquelles me focaliser quand j'ai du temps libre. Il y a beaucoup de ressources pour les débutants, mais grâce à l'immense capacité de personnaliser Emacs, il y a une explosion combinatoire de possibilités au niveau intermédiaire. J'ai hâte d'explorer mes questions et d'autres. C'est difficile de trouver et de naviguer dans les informations, donc si je commence avec des améliorations personnelles ou des questions concrètes de personnes spécifiques, c'est mieux que d'écrire isolément. Je suis sûre qu'en répondant aux questions individuelles, les points communs émergeront.
</p>

<p>
Je suis aussi fascinée par mes études sur d'autres sujets. J'apprends le français et je m'amuse tellement à bricoler mon environnement Emacs et mon <a href="https://sachachua.com/topic/french/#mon-apprentissage">processus d'apprentissage</a>. Un des conseils pour les débutants est de développer des îlots linguistiques : les mots autour d'un sujet passionnant. (Comme cet article-ci que j'écris en français pour me forcer à enrichir mon vocabulaire.) Je me demande comment visualiser ces choses… Par exemple, si je récupère des listes de mots triés par fréquence (peut-être en analysant la base de données <a href="https://lexique.org/databases/Lexique383/">lexique</a>), je peux créer une matrice de carrés… Hmm…
</p>

<p>
Mes obligations de maman diminuent au fur et à mesure que ma fille grandit. J'espère pouvoir consacrer plus de temps à explorer, à saisir et à partager des choses intéressantes.
</p>
</div>
</div>
<div><a href="https://sachachua.com/blog/2026/08/carnaval-d-emacs-d-aout-la-gestion-d-information-et-les-graphes-de-connaissances/index.org">View Org source for this post</a></div>
<p>You can <a href="https://social.sachachua.com/@sacha/statuses/01M170Q8N30823KE54NGBXBN1K" target="_blank">comment on Mastodon</a> or <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F08%2Fcarnaval-d-emacs-d-aout-la-gestion-d-information-et-les-graphes-de-connaissances%2F&amp;body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p></body></html>]]></content>
        <author>
            <name>Sacha Chua</name>
            <uri>https://sachachua.com/blog/category/emacs/feed/index.xml</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[TAONAW - Emacs and Org Mode: Emacs Config Gems - Part 5]]></title>
        <id>https://taonaw.com/2026/08/29/emacs-config-gems-part.html</id>
        <link href="https://taonaw.com/2026/08/29/emacs-config-gems-part.html"/>
        <updated>2026-08-29T12:34:14.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>Being a text editor (among other things), Emacs has quite a few tricks up its sleeve for making writing not just easier but also fun and seemingly magical. I call this section in my config “Wordcraft.”</p>
<h3>Emacs Dictionary</h3>
<p>When we need a dictionary, Emacs got us covered and then some. After version 28, Emacs connects to a dictionary as a two-step process by default:</p>
<ol>
<li>It tries to connect to a local host.</li>
<li>If it fails, it next connects to an online dictionary at <a href="https://www.dict.org">www.dict.org</a>,</li>
</ol>
<p>This workflow remains in place as long as <code>dictionary-server</code> is <code>nil</code>, which is the default. It’s a bit confusing (to me anyway), since “nil” might indicate that no dictionary is set up, but here it means we simply didn’t introduce any configuration to it.</p>
<p>The online version at dict.org contains the Webster dictionary from 1913<sup><a href="https://taonaw.com/categories/emacs-org-mode/#fn:1">1</a></sup>, which (if you follow the footnote) will start sounding like an excellent resource. What I didn’t know up until now however is that dict.org contains <em>more</em> than just this wonderful dictionary.</p>
<p>When you search for something like “US” with Emacs’ built-in dictionary, it checks this database and gives you results from:</p>
<ol>
<li>The Collaborative International Dictionary of English</li>
<li>WordNet</li>
<li>V.E.R.A. – Virtual Entity of Relevant Acronyms</li>
<li>The Free Online Dictionary of Computing (it has an entry as an ASCII character)</li>
</ol>
<p><a href="https://dict.org/bin/Dict?Form=Dict1&amp;Query=00-database-info&amp;Strategy=*&amp;Database=*">There are others</a>, like the CIA Factbook and the Bible, among others. All of those are at your fingertips, inside Emacs, from a simple quick search<sup><a href="https://taonaw.com/categories/emacs-org-mode/#fn:2">2</a></sup>.</p>
<p>The problem is that it’s an online dictionary. No internet, no dictionary. On my MacBook this can happen as I take it with me when I travel. Meanwhile, on Linux, I’m always online (desktop, wired connection).</p>
<p>Follow the footnote, and you will learn how to get that Webster dictionary (but just that, not the database at dict.org) working offline. As for me, I don’t want to set up an offline dictionary because macOS’s dictionary app<sup><a href="https://taonaw.com/categories/emacs-org-mode/#fn:3">3</a></sup> (which works fine offline) is good enough — a quick copy-paste and I’m done. So I configured my <code>dictionary-server</code> to point directly to dict.org, so Emacs doesn’t even search for a local one first.</p>
<p>Here is the setting itself, including the localhost option as a reference and a reminder:</p>
<div class="highlight"><pre><code class="language-lisp"><span><span><span>;; (set dictionary-server nil) default, search localhost and then dict.org</span>
</span></span><span><span><span>;; (setq dictionary-server "localhost") local only, no online search</span>
</span></span><span><span>  (<span>setq</span> dictionary-server <span>"dict.org"</span>)
</span></span></code></pre></div><h3>Hippie Expand</h3>
<p>I learned about <code>hippie-expand</code> through <a href="https://www.masteringemacs.org/article/text-expansion-hippie-expand">Mastering Emacs</a>. It’s one of those things that make Emacs magic. It uses different expansion functions (not to be confused with completion functions, which we will learn more about in a minute) to guess which word should come next in our text as we write, depending on which buffer we are in.</p>
<p>Following Mickey’s advice (linked above), I rebound this to <kbd>M-/</kbd>, which is bound to <code>dabbrev-expand</code> by default. It’s similar, but not as powerful (essentially it’s more basic; <code>hippie-expand</code> takes the functions that <code>dabbrev-expand</code> uses and builds on top of those with additional ones). Keep pressing <kbd>M-/</kbd> to flip through suggestions.</p>
<p>The catch is that you need to open the buffers you want it to look through. For example, if I want it to pull words from my personal journal while writing this post, I need to have my journal buffer open somewhere. If you’re one of the people who don’t turn off Emacs often, this is not a problem — just keep that in mind when you use it.</p>
<p>Turning it on (by replacing the command, using the same binding):</p>
<div class="highlight"><pre><code class="language-lisp"><span><span>(global-set-key [remap dabbrev-expand] <span>'hippie-expand</span>)
</span></span></code></pre></div><p>We also want to change the order in which it searches for things so it works better for us. For me, as a person who does less coding and more writing, I prefer it to look at Lisp-related functions toward the end. The same logic applies to global (all-buffers) lists vs those that operate on the visible buffer only. So our first <kbd>M-/</kbd> will give us suggestions from our current buffer, and as we keep pressing <kbd>M-/</kbd> and exhaust those, it will search all the other buffers we have open. for other suggestions<sup><a href="https://taonaw.com/categories/emacs-org-mode/#fn:4">4</a></sup>.</p>
<div class="highlight"><pre><code class="language-lisp"><span><span>(<span>setq</span> hippie-expand-try-functions-list
</span></span><span><span>      <span>'</span>(try-expand-dabbrev
</span></span><span><span>        try-expand-dabbrev-all-buffers
</span></span><span><span>        try-expand-dabbrev-from-kill
</span></span><span><span>        try-expand-line
</span></span><span><span>        try-expand-line-all-buffers
</span></span><span><span>        try-expand-list
</span></span><span><span>        try-expand-list-all-buffers
</span></span><span><span>        try-complete-file-name-partially
</span></span><span><span>        try-complete-file-name
</span></span><span><span>        try-expand-all-abbrevs
</span></span><span><span>        try-complete-lisp-symbol-partially
</span></span><span><span>        try-complete-lisp-symbol))
</span></span></code></pre></div><h3>ispell</h3>
<p>ispell is one of those things I set around the time I started using Emacs and never looked at again. I’ve made a mistake: I bound <code>ispell-region</code> to <kbd>M-$</kbd>. Those of you who use ispell often are probably perking an eyebrow: “huh? Why would you do that for? ispell does it by default!”</p>
<p>But somehow I didn’t know this. For <em>years</em> I highlighted whatever I needed to spell check, even single words. The default keybinding attributes <kbd>M-$</kbd> to <code>ispell-word</code>, which checks the word at the marker, <em>but if you have a region selected</em>, it spellchecks the region. I should have given Emacs more credit; today I know better.</p>
<p>Another really handy thing I did not know about ispell is its ability to complete words I’m unsure of.
But before we go on, please note that word completion (and completion functions) in Emacs is a big topic that I just spent a whole week on, and what I’m going to discuss here is just the tip of the iceberg.</p>
<p>First, the useful built-in completion built into ispell itself: l for lookup. Say you forgot how to write “psychiatry”. You type what you know — “psy” — and then call ispell, and as the suggestions show up, press <kbd>l</kbd>. ispell will ask you what to complete, along with a wildcard, so add <kbd>*</kbd> and then <kbd>y</kbd> so you have “psy*y”. ispell will show you what words start with psy and have whatever letters after but end with y. Useful!</p>
<p>Another thing I didn’t know: use <kbd>R</kbd> for query replace. This is good when you make the same mistake across several words. For example, when I wrote this post, I originally capitalized every ispell as Ispell. No problem. Stand on one of those, call ispell, press R to replace, and change it to ispell. Now you are prompted if you want to change all of them one by one, or, just hit <kbd>!</kbd> and get it over with in one swoop.</p>
<p>With those quick tips, now back to completion functions.</p>
<p>Years ago, when I started using Emacs, I installed <a href="https://company-mode.github.io/">company</a> with its minimal default settings and called it a day. It’s been working OK, and up until I started writing this post, I didn’t realize how much about Emacs completion-at-point functions (capf). Prot <a href="https://www.youtube.com/watch?v=x3rqjpNm3e0">has a good video about this</a>, which I’m still chewing through, but basically for ispell the magic happens when you stand on a word you don’t know how to finish and ask Emacs to suggest completions.</p>
<p>To understand how this works, Let’s say we don’t know if water is written as watter or watar, as a simple example.</p>
<ol>
<li>We write “wate” and we ask Emacs to complete the word by calling completion with ispell with <kbd>M-C</kbd> <kbd>i</kbd>. Because the only word that exists (in English anyway) that starts with “wate” is water, Emacs will complete it for us to “water” automatically.</li>
<li>Now, while standing on the word “water,” we call completion again. Emacs informs us: <strong>complete, but not unique.</strong> In other words: the word water is real and complete (we have it spelled right) <em>but</em> there are other words that start with water.</li>
<li>While still standing on “water”, we call complete yet again. Emacs goes “OK. So you know it’s a word that exists, and you also know there are other words that start with water, so if you’re calling complete again, you probably want to see the other words that start with water.”</li>
</ol>
<p>Bam, we have a buffer full of words that start with water, like waterboard or watermelon. We can select a word from this menu, even though it’s a bit weird — by going down or up with <kbd>M-⇡</kbd> and <kbd>M-⇣</kbd>, then insert the word we want with <kbd>M-Ret</kbd><sup><a href="https://taonaw.com/categories/emacs-org-mode/#fn:5">5</a></sup>.</p>
<p>This is nice, but usually you want to complete words that you already know, or kind of know. If I type “psych,” I don’t need a whole buffer of words if I’m looking for “psychology”; I just slowly add letters until I find what I need — and this is exactly what packages like company or corfu<sup><a href="https://taonaw.com/categories/emacs-org-mode/#fn:6">6</a></sup> do.</p>
<h3>Harper, languagetool and abbrevs</h3>
<p>Let me just remind you of those for now and finish up this post. We will dig into those more next time.</p>
<div class="footnotes">
<hr>
<ol>
<li>
<p><a href="https://irreal.org/blog/?p=9035">This post from irreal</a> is a good place to get a bit more understanding of how to use a local dictionary, a copy of the online one, and why. If you read it, you may find your way into Irreal’s older post and then this wonderful gem he found: <a href="http://jsomers.net/blog/dictionary">You’re probably using the wrong dictionary</a>. The way these posts are written, I feel it makes more sense to read them in the reverse order I presented here, inviting more exploration as you go — however, if you’re short on time or patience, I recommend you read at least that last one, especially if you enjoy writing.&nbsp;<a href="https://taonaw.com/categories/emacs-org-mode/#fnref:1">↩︎</a></p>
</li>
<li>
<p>As I was learning this, this next question popped into my mind: can we then look for a definition in Wikipedia and the <a href="https://www.urbandictionary.com">Urban Dictionary</a> as well? The straight answer: yes (it’s Emacs, duh). The full answer: it’s complicated. We can’t integrate it into the results buffer from <code>dictionary-search</code> directly, but what I can do is have it inside a “wrapper” buffer that will contain the dict.org definition with the Wikipedia and Urban Dictionary below it. For now, I’m fine with just using <code>eww-search-words</code> which initiates a search with Duck Duck Go, which already pulls Wikipedia search results near the top (Wikipedia itself forces JavaScript usage, which blocks Eww)&nbsp;<a href="https://taonaw.com/categories/emacs-org-mode/#fnref:2">↩︎</a></p>
</li>
<li>
<p>By the way, something fun I discovered while researching: we can call the macOS dictionary using <code>dict://</code> (as a URL link), and we can build a function in Emacs for it if we want. Further — we could create something like this for offline situations only, but this is not needed at least in my case. Worth mentioning: there’s also the <a href="https://github.com/xuchunyang/osx-dictionary.el">osx-dictionary</a> package, which bridges Emacs and the built-in dictionary app on macOS. But why, when you have a different (and arguably better) version built in?&nbsp;<a href="https://taonaw.com/categories/emacs-org-mode/#fnref:3">↩︎</a></p>
</li>
<li>
<p><code>hippie-expand</code> doesn’t really have a help file that lists all the functions, though I think I’ve listed most of those if not all above. Check out the package directly if you want to read more about the functions; they are described in comments at the top of the code. Online, <a href="https://github.com/emacs-mirror/emacs/blob/master/lisp/hippie-exp.el">it’s here</a>, or you can navigate your way to the package inside Emacs with <kbd>C-h</kbd><kbd>k</kbd><kbd>M-/</kbd>, and then go from there to the package.&nbsp;<a href="https://taonaw.com/categories/emacs-org-mode/#fnref:4">↩︎</a></p>
</li>
<li>
<p>Turns out you don’t have to do it this way: you can switch to the completion buffer, and then you can move around with arrows, including up and down, and select what you want with Enter like a normal human being. You could also add <code>(setq completion-auto-select t)</code> to your init for this switch to happen automatically whenever you press <kbd>C-M-i</kbd>.&nbsp;<a href="https://taonaw.com/categories/emacs-org-mode/#fnref:5">↩︎</a></p>
</li>
<li>
<p>As mentioned earlier, these packages and their functions go beyond what I’ll write in this post, but a few things are worth mentioning: company is much older then corfu, which came into existence after Emacs implemented completion-at-point functions; as a result, corfu is based on capf, and doesn’t come with anything “extra” out of the box besides a few option to display completion in a dynamic matter. Company, on the other hand, is a much bigger package that has a whole backend to it, complete with a lot of additional functions. I ended up installing and trying corfu, with Emacs’ native functions, and then installed cape (completion at point extensions) makes more functions available in a buffer than you’d usually have. What does that mean? Well, say you’re writing something in org-mode. the cap functions you have available do not include, for example, file names and paths, which you might want; these functions will be available to you when you are in a shell buffer (usually) — it’s Emacs being Emacs and trying to be useful. So with cape, you can ask for that function to be available in org-mode as well.&nbsp;<a href="https://taonaw.com/categories/emacs-org-mode/#fnref:6">↩︎</a></p>
</li>
</ol>
</div>
</body></html>]]></content>
        <author>
            <name>TAONAW - Emacs and Org Mode</name>
            <uri>https://taonaw.com/categories/emacs-org-mode/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Donovan R.: My way of handling knowledge (Emacs Carnival - The Search for Knowledge)]]></title>
        <id>https://donovan-ratefison.mg/2026/08/29/My-way-of-handling-knowledge-Emacs-Carnival-The-Search-for-Knowledge/</id>
        <link href="https://donovan-ratefison.mg/2026/08/29/My-way-of-handling-knowledge-Emacs-Carnival-The-Search-for-Knowledge/"/>
        <updated>2026-08-29T12:00:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><img src="https://donovan-ratefison.mg/2026/08/29/My-way-of-handling-knowledge-Emacs-Carnival-The-Search-for-Knowledge/search-for-knowledge.png" width="600" title="Search for knowledge" alt="The Search for Knowledge">

<p><em>This post is my participation in this month’s <a href="https://www.chiply.dev/post-august-emacs-carnival">Emacs Carnival</a>, on the theme of The Search for Knowledge.</em></p>
<h2><a href="https://donovan-ratefison.mg/#About-my-current-workflow"></a>About my current workflow</h2><p>My way of handling notes and knowledge has evolved a lot over time. I’ve written about my note-taking journey in <a href="https://donovan-ratefison.mg/2024/07/20/My-note-taking-journey/">this blog post</a>.<br>As of today, my knowledge management sauce is composed of Emacs and <a href="https://vakana.mg/">Vakana.mg</a>. I’ve been using Logseq less and less over the past few months.</p>
<h3><a href="https://donovan-ratefison.mg/#Indoor"></a>Indoor</h3><p>In my day-to-day computer usage, I have the Emacs client open all day long with a scratch buffer ready for anything. I dump everything into the scratch buffer, remove or add paragraphs as needed and use <a href="https://elpa.gnu.org/packages/vundo.html">vundo</a> for time traveling. I use no structure except some dashes as separation between topics.<br>Whether it’s to capture fleeting insights or mold prompts for LLMs, the scratch buffer acts as a buffer between my mind and the computer (notes, prompts, etc.).<br>If something interesting is worth saving, then I save it. I use <a href="https://protesilaos.com/emacs/denote">Denote</a> for that.<br>If I’m not using my computer, I use pen and paper to capture thoughts and ideas, which I extract later on using a VLM if it’s worth saving. I talk about the process in <a href="https://donovan-ratefison.mg/2026/03/01/My-2026-Note-Taking-Workflow/">this post</a>.</p>
<h3><a href="https://donovan-ratefison.mg/#Outdoor"></a>Outdoor</h3><p>For external capture I use <a href="https://vakana.mg/">Vakana.mg</a>, my own offline mobile app. It solves the problem of taking photos, writing notes and commenting on them at the same time.<br>Very practical when I’m in the wild documenting plants. When I’m back home, I can export to an Org file from my phone and send it to my computer using <a href="https://localsend.org/">LocalSend</a>.</p>
<h2><a href="https://donovan-ratefison.mg/#The-Search-for-Knowledge"></a>The Search for Knowledge</h2><h3><a href="https://donovan-ratefison.mg/#Retrieving"></a>Retrieving</h3><p>I don’t bother too much about retrieving. In Emacs, I either just open <a href="https://protesilaos.com/emacs/denote">Denote</a> or use <code>project-find-regexp</code> to reach whatever I want. Vakana.mg’s search also does fuzzy finding inside note contents.</p>
<h3><a href="https://donovan-ratefison.mg/#On-knowledge-graphs"></a>On knowledge graphs</h3><p>From what I’ve learned and in my own experience, retention of knowledge works better when the owner defines the visual representation of the information to retain, so it matches the way their mind works, and not the other way around (by letting the patterns emerge).<br>Putting direct intent into the graph’s shape makes the retention of the knowledge stronger. It’s the same mechanism as “charging an image” for better retention in the <a href="https://artofmemory.com/blog/mind-palace/">Memory Palace</a> technique.<br>Vakana.mg’s visual design works better for me because it’s easier to traverse nodes in a linear way. And custom threads representing specific topics make for clean reading and understanding.</p>
<h3><a href="https://donovan-ratefison.mg/#Emacs-Packages-for-Knowledge-Management"></a>Emacs Packages for Knowledge Management</h3><p>I like <a href="https://www.gnu.org/software/hyperbole/">Hyperbole</a> and <a href="https://www.orgroam.com/">org-roam</a> but I don’t use them at all. I use <a href="https://protesilaos.com/emacs/denote">Denote</a> and plain org files. The simpler, the better for me. Even the information structure is very simple: one file = one topic. The length may vary.</p>
<h3><a href="https://donovan-ratefison.mg/#Where-do-all-my-notes-come-from"></a>Where do all my notes come from?</h3><p>I don’t copy-paste much from other sources. Most of what I write comes from my brain. Either by digesting some books or through insights and observations.<br>The exceptions are pictures I take with my phone.</p>
<h3><a href="https://donovan-ratefison.mg/#What%E2%80%99s-next-What-I-wish-to-have"></a>What’s next? What I wish to have</h3><ul>
<li><p>Resurfacing random notes.<br>I write more notes than I can review. A lot of notes are buried forever. In my spare time, I want to be able to go through my old collected notes. I added the feature in Vakana.mg. I don’t have the system in place yet in Emacs.</p>
</li>
<li><p>Whiteboard inside Emacs.<br><a href="https://www.emacswiki.org/emacs/ArtistMode">Artist mode</a> is really good but it’s not that great for sharing because not everyone is a fan of ASCII art. Especially in a professional context. For that, I rely heavily on <a href="https://excalidraw.com/">Excalidraw</a> for diagrams. Now that we have the <a href="https://github.com/minad/emacs-canvas-patch">Emacs canvas</a>, I really hope someone will soon build something similar to Excalidraw within Emacs.</p></li></ul></body></html>]]></content>
        <author>
            <name>Donovan R.</name>
            <uri>https://donovan-ratefison.mg/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Meta Redux: Smarter Form Targeting Is Not Coming to CIDER]]></title>
        <id>https://metaredux.com/posts/2026/08/29/smarter-form-targeting-is-not-coming-to-cider.html</id>
        <link href="https://metaredux.com/posts/2026/08/29/smarter-form-targeting-is-not-coming-to-cider.html"/>
        <updated>2026-08-29T06:37:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>A couple of days ago I wrote that <a href="https://metaredux.com/posts/2026/08/26/smarter-form-targeting-is-coming-to-cider.html">smarter form targeting was coming to CIDER</a>,
and I ended that post by asking whether I’d got the resolution rules right and
whether anything still surprised people. I got an answer. CIDER 2.1 will ship
with the classic behaviour intact.</p>



<p>This is not a sad story, though. The detour turned up a bug that was quietly
mangling people’s comments, and CIDER came out of it better than it went in.</p>

<h2>What I was actually after</h2>

<p>The targeting change wasn’t really about cursor positions. What I wanted was
for <em>every</em> CIDER command that operates on a form to behave the same way,
without adding yet another command to get there. CIDER has an enormous surface
of evaluation commands, and every “at point” variant I could have added would
have made that worse. If the existing commands simply resolved the form you
meant, newcomers would have had fewer commands to learn, not more.</p>

<p>That was the bet: consistency by redefinition rather than by addition. In
hindsight it was the wrong bet for a project this old. Fifteen years in, the
existing behaviour isn’t an implementation detail I get to tidy up - it’s
the contract. And as it turned out, the redefinition wasn’t nearly as
transparent as I’d convinced myself it was.</p>

<h2>The feedback</h2>

<p>Several users, including CIDER’s co-maintainer Sashko Yakushev, voiced
concerns and flagged issues I’d overlooked while playing with this initially.
The most important one: inspection is just another flavour of evaluation, and
people inspect bare symbols constantly. If it got the same treatment, the
disruption would be real enough to run a fork over.</p>

<p>My instinct was that this was an edge case, so I measured it instead of
arguing. Across every cursor position in a buffer, only three rules actually
differ, and the flagship flow - type a form, hit <code class="language-plaintext highlighter-rouge">C-x C-e</code> - is identical
under both. Two of those three were fine. The third was this one:</p>

<p><img src="https://metaredux.com/assets/images/cider-closing-paren.gif" alt="Cursor on a closing paren: the classic rules evaluate the last form inside, smart targeting evaluates the whole enclosing call"></p>

<p>The cursor doesn’t move between those two evaluations. That’s the same
position, twice, and the answers differ - <code class="language-plaintext highlighter-rouge">"b"</code> under the classic rules,
<code class="language-plaintext highlighter-rouge">"ab"</code> under the new ones.</p>

<p>I’d filed “cursor on a closing paren” as an oddity nobody hits deliberately.
But think about when you land there: you finish typing the last thing inside
a form, and the cursor is now sitting on the <code class="language-plaintext highlighter-rouge">)</code>. For someone inspecting
symbols all day, that fires constantly. And the classic answer isn’t
arbitrary either - as Sashko put it, the rule of thumb is “whatever
<code class="language-plaintext highlighter-rouge">paredit-backward</code> jumps back to”, which is a better description of the
tradition than anything I’d written down.</p>

<h2>Why the tradition exists in the first place</h2>

<p>Here’s the part I under-weighted, and it’s worth spelling out for anyone who
finds “the form <em>before</em> the cursor” arbitrary.</p>

<p>Emacs form navigation overwhelmingly leaves the cursor <em>after</em> a form.
<code class="language-plaintext highlighter-rouge">C-M-f</code> (<code class="language-plaintext highlighter-rouge">forward-sexp</code>) moves over the next form and stops just past its
closing delimiter. <code class="language-plaintext highlighter-rouge">C-M-e</code> (<code class="language-plaintext highlighter-rouge">end-of-defun</code>) leaves you after the whole
top-level form. <code class="language-plaintext highlighter-rouge">C-M-n</code> (<code class="language-plaintext highlighter-rouge">forward-list</code>) does the same for the enclosing
list. Paredit’s <code class="language-plaintext highlighter-rouge">paredit-forward</code> behaves the same way, and so does typing:
finish a form and the cursor is, by definition, right after it.</p>

<p>So “evaluate the preceding form” isn’t a quirk - it <em>composes</em> with how you
already move around. Navigate forward over a form, evaluate it. Type a form,
evaluate it. The cursor is already in the right place, every time.</p>

<p>Getting <em>onto</em> a form instead takes deliberate effort: <code class="language-plaintext highlighter-rouge">C-M-b</code>
(<code class="language-plaintext highlighter-rouge">backward-sexp</code>), <code class="language-plaintext highlighter-rouge">C-M-a</code> (<code class="language-plaintext highlighter-rouge">beginning-of-defun</code>), <code class="language-plaintext highlighter-rouge">paredit-backward</code>, or a
jump package like <code class="language-plaintext highlighter-rouge">avy</code>. All perfectly good tools, but you have to reach for
them - unless you’re clicking around with a mouse, in which case the cursor
lands wherever you pointed and “the form before the cursor” genuinely is
useless. Which, I suspect, is exactly the workflow difference behind this
whole argument.</p>

<h2>What CIDER got instead</h2>

<p>The half of the idea that was never controversial is still there, as commands
you opt into rather than a new meaning for keys you already use. Every
operation now has an “at point” variant, not just eval and tap:
<code class="language-plaintext highlighter-rouge">cider-inspect-sexp-at-point</code>, <code class="language-plaintext highlighter-rouge">cider-pprint-eval-sexp-at-point</code>,
<code class="language-plaintext highlighter-rouge">cider-macroexpand-1-at-point</code>, <code class="language-plaintext highlighter-rouge">cider-macroexpand-all-at-point</code>,
<code class="language-plaintext highlighter-rouge">cider-format-edn-sexp-at-point</code>, <code class="language-plaintext highlighter-rouge">cider-insert-sexp-at-point-in-repl</code>.</p>

<p>Three ways to say which form you mean, and now every command supports all of
them:</p>

<p><img src="https://metaredux.com/assets/images/cider-form-selection.gif" alt="The same expression evaluated three ways: the preceding form, the form at the cursor, and the enclosing top-level form"></p>

<p>The at-point commands fall back to the preceding form when there’s nothing to
point at, so they’re drop-in replacements rather than a separate mode of
working. Which means anyone who wanted smart targeting can simply have it:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="nv">with-eval-after-load</span> <span class="ss">'cider-mode</span>
  <span class="p">(</span><span class="nv">define-key</span> <span class="nv">cider-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"C-x C-e"</span><span class="p">)</span> <span class="nf">#'</span><span class="nv">cider-eval-sexp-at-point</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">define-key</span> <span class="nv">cider-mode-map</span> <span class="p">(</span><span class="nv">kbd</span> <span class="s">"C-c C-e"</span><span class="p">)</span> <span class="nf">#'</span><span class="nv">cider-eval-sexp-at-point</span><span class="p">))</span>
</code></pre></div></div>

<p>Two lines, no hidden mode, and everyone else’s fingers keep working. This is
what I should have shipped in the first place, and it’s what the
<a href="https://docs.cider.mx/cider/usage/code_evaluation.html">manual</a> now
recommends. Yes, it’s more commands than I wanted. It’s also the version that
doesn’t break anyone.</p>

<p>Macroexpansion is the one place a bit of cleverness survived on its own
merits, because an expansion needs a call form. Stand on a bare symbol and
<code class="language-plaintext highlighter-rouge">cider-macroexpand-1-at-point</code> widens to the call around it, since expanding a
lone symbol is never what anyone meant.</p>

<h2>The bug at the bottom of the hole</h2>

<p>While unifying the plumbing I found this, which is much worse than anything
form targeting was ever guilty of. Put the cursor at the end of a comment:</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="k">defn</span><span class="w"> </span><span class="n">foo</span><span class="w"> </span><span class="p">[])</span><span class="w">
</span><span class="c1">;; a comment|</span><span class="w">
</span></code></pre></div></div>

<p>CIDER answered <code class="language-plaintext highlighter-rouge">comment</code>. Not the <code class="language-plaintext highlighter-rouge">(comment ...)</code> form - the <em>word</em>, lifted
out of your prose, because sexp motion has no notion of comments once the
cursor is inside one and happily reads the words as symbols.</p>

<p>For evaluation that produced a puzzling error. For the in-place macroexpansion
commands, which <em>replace</em> the region they resolved, it did this:</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre><code><span class="c1">;; a comment          -&gt;   ;; a EXPANDED&lt;comment&gt;</span><span class="w">
</span><span class="p">(</span><span class="nb">+</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w"> </span><span class="c1">; hey         -&gt;   (+ 1 2)          ; EXPANDED&lt;hey&gt;</span><span class="w">
</span></code></pre></div></div>

<p>It rewrote the comment. That bug has been in CIDER for years, and I only found
it because I went looking at the primitives while cleaning up after myself.</p>

<p>Then I checked the neighbours, and this is my favourite part of the whole
episode: <strong>SLIME, SLY and Emacs Lisp itself all still do this.</strong> Both Lisp
environments use a bare <code class="language-plaintext highlighter-rouge">backward-sexp</code>, and if you put the cursor after
<code class="language-plaintext highlighter-rouge">(+ 1 2) ; hey</code> in any Emacs Lisp buffer and ask for the preceding sexp, you
get <code class="language-plaintext highlighter-rouge">hey</code>. CIDER now steps out of the comment first, which as far as I can
tell makes it the only one of the family that gets this right.<sup><a href="https://metaredux.com/#fn:1">1</a></sup></p>

<h2>One idea worth stealing</h2>

<p>The same survey turned up something CIDER was missing. SLY briefly flashes the
region it compiled, so you <em>see</em> what it acted on. That’s a direct answer to
the confusion this whole saga was about - “which form did it just take?” -
and it changes nothing about what gets taken.</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="k">setq</span> <span class="nv">cider-flash-evaluated-region</span> <span class="no">t</span><span class="p">)</span>
</code></pre></div></div>

<p>Off by default, because I’ve learned my lesson about switching things on for
everyone. But if the targeting rules ever puzzle you, turn it on for a day.</p>

<h2>The moral</h2>

<p>In the original post I described <code class="language-plaintext highlighter-rouge">cider-form-targeting</code>, the escape hatch back
to the classic rules, as “living on borrowed time” - clutter left over from a
plan I’d since reversed, which I was itching to delete.</p>

<p>That option is the only reason the conversation stayed a conversation. Its
existence made the objection “please don’t remove the fallback” rather than
“I’m forking CIDER”, and I very nearly removed it before anyone had tried the
change.</p>

<p>Every mistake is a learning experience for me, and this one was cheap: nothing
had shipped, so the whole thing cost a few days and some rewriting. The
testing and feedback cycle worked exactly as it should have - people tried
something on <code class="language-plaintext highlighter-rouge">master</code>, told me plainly what was wrong with it, and the result
is better than either what I proposed or what we had before. Everyone gets
the behaviour they want, and CIDER lost a text-eating bug on the way.</p>

<p>Thanks to everyone who took the time to tell me I was wrong.</p>

<p>That’s all I have for you today. Keep hacking!</p>

<div class="footnotes">
  <ol>
    <li>
      <p>If you’re an Emacs maintainer reading this: <code class="language-plaintext highlighter-rouge">elisp--preceding-sexp</code> has the same behaviour, and I’d be happy to be told why it’s intentional.&nbsp;<a href="https://metaredux.com/#fnref:1">↩</a></p>
    </li>
  </ol>
</div></body></html>]]></content>
        <author>
            <name>Meta Redux</name>
            <uri>https://metaredux.com/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Protesilaos: Emacs: completion-preview-mode and the Completions buffer (Emacs 31)]]></title>
        <id>https://protesilaos.com/codelog/2026-08-29-emacs-completion-preview-mode/</id>
        <link href="https://protesilaos.com/codelog/2026-08-29-emacs-completion-preview-mode/"/>
        <updated>2026-08-29T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>Raw link: <a href="https://www.youtube.com/watch?v=8V4ZyEL_i-s">https://www.youtube.com/watch?v=8V4ZyEL_i-s</a></p>
         
         <p>The built-in <code class="language-plaintext highlighter-rouge">completion-preview-mode</code> lets us expand what we type in
a minimalist, distraction-free way. It can also be combined with the
default Completions buffer to show all the available options. The
Completions buffer has several nice features, starting with Emacs 31,
which make this an excellent combination that does not require any
third party packages (though packages can still augment the overall
experience). I also mention the Emacs 31 ‘newcomers-presets’ theme,
which configures some of these options as well: it is a great way to
start your Emacs configuration.</p>

<h2>Sample configuration</h2>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="nb">use-package</span> <span class="nv">completion-preview</span>
  <span class="ss">:ensure</span> <span class="no">nil</span>
  <span class="ss">:demand</span> <span class="no">t</span>
  <span class="ss">:bind</span>
  <span class="p">(</span> <span class="ss">:map</span> <span class="nv">completion-preview-active-mode-map</span>
    <span class="p">(</span><span class="s">"M-i"</span> <span class="o">.</span> <span class="nv">completion-preview-insert-word</span><span class="p">)</span>
    <span class="p">(</span><span class="s">"M-n"</span> <span class="o">.</span> <span class="nv">completion-preview-next-candidate</span><span class="p">)</span>
    <span class="p">(</span><span class="s">"M-p"</span> <span class="o">.</span> <span class="nv">completion-preview-prev-candidate</span><span class="p">)</span>
    <span class="p">(</span><span class="s">"M-&lt;return&gt;"</span> <span class="o">.</span> <span class="nv">completion-preview-insert</span><span class="p">)</span>
    <span class="c1">;; With TAB we effectively defer to the *Completions* buffer to</span>
    <span class="c1">;; show more completion candidates at once.</span>
    <span class="p">(</span><span class="s">"&lt;tab&gt;"</span> <span class="o">.</span> <span class="nv">completion-preview-complete</span><span class="p">))</span>
  <span class="ss">:config</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">completion-preview-minimum-symbol-length</span> <span class="mi">2</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">with-eval-after-load</span> <span class="ss">'org</span>
    <span class="p">(</span><span class="nv">add-to-list</span> <span class="ss">'completion-preview-commands</span> <span class="nf">#'</span><span class="nv">org-self-insert-command</span><span class="p">))</span>
  <span class="p">(</span><span class="nv">global-completion-preview-mode</span> <span class="mi">1</span><span class="p">))</span>

<span class="c1">;; I have a more detailed configuration (and explanation) that works</span>
<span class="c1">;; with Emacs 31 built-in completion capabilities:</span>
<span class="c1">;; https://protesilaos.com/codelog/2026-07-29-emacs-default-minibuffer-completion-overview/.</span>
<span class="p">(</span><span class="nb">use-package</span> <span class="nv">minibuffer</span>
  <span class="ss">:ensure</span> <span class="no">nil</span>
  <span class="ss">:demand</span> <span class="no">t</span>
  <span class="ss">:bind</span>
  <span class="p">(</span> <span class="ss">:map</span> <span class="nv">completion-in-region-mode-map</span>
    <span class="p">(</span><span class="s">"M-i"</span> <span class="o">.</span> <span class="nv">minibuffer-choose-completion</span><span class="p">)</span>
    <span class="p">(</span><span class="s">"M-n"</span> <span class="o">.</span> <span class="nv">minibuffer-next-completion</span><span class="p">)</span>
    <span class="p">(</span><span class="s">"M-p"</span> <span class="o">.</span> <span class="nv">minibuffer-previous-completion</span><span class="p">))</span>
  <span class="ss">:config</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">completions-format</span> <span class="ss">'one-column</span><span class="p">)</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">completions-max-height</span> <span class="mi">12</span><span class="p">)</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">completion-auto-help</span> <span class="no">t</span><span class="p">)</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">completion-auto-select</span> <span class="no">nil</span><span class="p">)</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">minibuffer-visible-completions</span> <span class="no">t</span><span class="p">)</span>
  <span class="p">(</span><span class="k">setq</span> <span class="nv">completion-eager-update</span> <span class="no">t</span><span class="p">))</span>
</code></pre></div></div>

<p>Finally, the <code class="language-plaintext highlighter-rouge">newcomers-presets</code> theme is an excellent way to start
your configuration. It is available as part of Emacs 31. Put this at
the top of your <code class="language-plaintext highlighter-rouge">init.el</code>:</p>

<div class="language-elisp highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="nv">load-theme</span> <span class="ss">'newcomers-presets</span><span class="p">)</span>
</code></pre></div></div>
        </body></html>]]></content>
        <author>
            <name>Protesilaos</name>
            <uri>https://protesilaos.com/codelog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[James Cherti: Measuring Emacs startup time more accurately than the built-in emacs-init-time]]></title>
        <id>https://www.jamescherti.com/measuring-emacs-startup-time/</id>
        <link href="https://www.jamescherti.com/measuring-emacs-startup-time/"/>
        <updated>2026-08-28T14:38:01.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>As an Emacs configuration grows, startup time can gradually increase. Measuring that increase accurately makes it easier to identify regressions. The majority of Emacs users use <code>emacs-init-time</code>. However, this built-in function does not measure all the work performed during startup.</p>



<p>Emacs startup proceeds through several stages: it loads the early init and regular init files, processes command-line options, sets <code>after-init-time</code>, runs <code>emacs-startup-hook</code>, performs additional initial-frame setup such as applying frame parameters from the configuration, and then runs <code>window-setup-hook</code> after the initial frame parameters have been configured. This makes <code>window-setup-hook</code> a useful place to record a broader startup-time measurement.</p>



<p>For a more accurate startup-time measurement, add the following Emacs Lisp code to <code>early-init.el</code> or <code>init.el</code>:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Emacs startup proceeds through several stages: it loads the early init and</span>
<span class="hljs-comment">;; regular init files, processes command-line options, sets after-init-time,</span>
<span class="hljs-comment">;; runs emacs-startup-hook, performs additional initial-frame setup such as</span>
<span class="hljs-comment">;; applying frame parameters from the configuration, and then runs</span>
<span class="hljs-comment">;; window-setup-hook after the initial frame parameters have been configured.</span>
<span class="hljs-comment">;; This makes window-setup-hook a useful place to record a broader startup-time</span>
<span class="hljs-comment">;; measurement.</span>
<span class="hljs-comment">;;</span>
<span class="hljs-comment">;; URL: https://www.jamescherti.com/measuring-emacs-startup-time/</span>
(<span class="hljs-name">defvar</span> my-recorded-startup-time-message <span class="hljs-literal">nil</span>
  <span class="hljs-string">"Stores the formatted string of the Emacs startup metrics."</span>)

(<span class="hljs-name">defun</span> my-record-startup-time ()
  <span class="hljs-string">"Calculate and record the elapsed startup time.
This function records the time when `window-setup-hook' runs."</span>
  (<span class="hljs-name">setq</span> my-recorded-startup-time-message
        (<span class="hljs-name">format</span> <span class="hljs-string">"Emacs loaded in %.2f seconds (Init time: %.2fs) with %d garbage collections."</span>
                (<span class="hljs-name">float-time</span> (<span class="hljs-name">time-since</span> before-init-time))
                (<span class="hljs-name">float-time</span> (<span class="hljs-name">time-subtract</span> after-init-time before-init-time))
                gcs-done))
  <span class="hljs-comment">;; Output to the *Messages* buffer during the initial launch</span>
  (<span class="hljs-name">message</span> <span class="hljs-string">"%s"</span> my-recorded-startup-time-message))

(<span class="hljs-name">defun</span> my-display-startup-time ()
  <span class="hljs-string">"Display the previously recorded Emacs startup time in the echo area."</span>
  (<span class="hljs-name">interactive</span>)
  (<span class="hljs-name">if</span> my-recorded-startup-time-message
      (<span class="hljs-name">message</span> <span class="hljs-string">"%s"</span> my-recorded-startup-time-message)
    (<span class="hljs-name">message</span> <span class="hljs-string">"Startup time was not recorded."</span>)))

<span class="hljs-comment">;; Read startup summary:</span>
<span class="hljs-comment">;; https://www.gnu.org/software/emacs/manual/html_node/elisp/Startup-Summary.html</span>
(<span class="hljs-name">add-hook</span> 'window-setup-hook #'my-record-startup-time <span class="hljs-number">99</span>)</code></span></pre>


<p>This provides a broader measurement than <code>M-x emacs-init-time</code> because it continues measuring after <code>after-init-time</code> has been set, including subsequent startup processing through <code>window-setup-hook</code>.</p>



<p>A depth of 99 places the function near the end of <code>window-setup-hook</code>. This makes the measurement less likely to omit work performed by other functions on the same hook.</p>



<p>The variable <code>gcs-done</code> tracks the total number of garbage collections during the session. Because this function formats and saves the message string during <code>window-setup-hook</code>, it locks in the exact number of garbage collections triggered during the startup phase, giving you a metric of memory allocation bottlenecks during initialization. Ideally, it should be 0 or 1.</p>



<p>Capturing the time the UI finishes rendering provides a reliable metric to track down performance regressions. Once Emacs has finished loading, you can retrieve the recorded startup time at any point during your session. Execute <code>M-x my-display-startup-time</code> to print the exact initialization metrics in the echo area. This interactive command ensures the startup data remains accessible even if the initial message is cleared from the screen by subsequent buffer activity.</p>
<div class="yarpp yarpp-related yarpp-related-rss yarpp-template-list">

<h3>Related posts:</h3><ol>
<li><a href="https://www.jamescherti.com/easysession-el-persist-restore-emacs-session/">easysession.el: Easily persist and restore Emacs sessions (windows, tab-bar, file buffers, scratch, Dired, narrowing, indirect buffers/clones, Magit buffers...); a robust desktop.el replacement</a></li>
<li><a href="https://www.jamescherti.com/emacs-evil-mode-restore-line-column-mark/">Emacs Evil Mode: How to restore both the line and column number of a mark, not just the line number</a></li>
<li><a href="https://www.jamescherti.com/emacs-highlight-keywords-like-todo-fixme-note/">Emacs: Highlighting Codetags Like TODO, FIXME, BUG, NOTE...</a></li>
<li><a href="https://www.jamescherti.com/emacs-compile-angel-byte-native-compile/">The compile-angel Emacs package: Byte-compile and Native-compile Emacs Lisp libraries Automatically</a></li>
<li><a href="https://www.jamescherti.com/emacs-ultisnips-mode-edit-snippets-files/">ultisnips-mode.el - An Emacs major mode for editing Ultisnips snippet files (*.snippets files)</a></li>
<li><a href="https://www.jamescherti.com/essential-emacs-packages/">Must-have Emacs Packages for Efficient Software Development and Text Editing</a></li>
<li><a href="https://www.jamescherti.com/emacs-quick-fasd/">quick-fasd.el - Integrate Fasd for fast file and directory navigation in Emacs</a></li>
</ol>
</div>
</body></html>]]></content>
        <author>
            <name>James Cherti</name>
            <uri>https://www.jamescherti.com</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Andros Fenollosa: How I organize my Emacs configuration]]></title>
        <id>http://en.andros.dev/blog/dc7db35a/how-i-organize-my-emacs-configuration/</id>
        <link href="http://en.andros.dev/blog/dc7db35a/how-i-organize-my-emacs-configuration/"/>
        <updated>2026-08-28T13:28:41.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>I am not going to share my Emacs configuration, but the way I keep it organized. I do not think I am discovering anything new, but it might be useful for someone who is starting to organize their configuration and does not know where to begin.</p>
<p>I do not use any framework (neither Doom nor Spacemacs) nor the literate approach of Org mode. Everything is vanilla Emacs and each file is an area of responsibility, while <code>init.el</code> acts as the index.</p>
<p>Why vanilla and not a framework? Frameworks decide for you. They lend you a huge configuration that you do not understand and that is hard to tweak. For me, the literate approach of Org is one more layer where something can break. I prefer the simplicity of the file I edit being the file that runs. The less magic, the fewer things that break.</p>
<p>So, inside my configuration folder (<code>~/.emacs.d/</code>), I have the following files:</p>
<ul>
<li>"init.el": the index of my configuration. It loads the rest of the files in order.</li>
<li>"core.el": base Emacs behavior, such as handling backups, paths, clipboard, etc.</li>
<li>"functions.el": my personal scripts and macros.</li>
<li>"packages.el": repository configuration, such as MELPA (among others).</li>
<li>"ui.el": everything related to visual aspects: theme, modeline, typography, visual behavior, etc.</li>
<li>"ide.el": LSP, debugger, linters and programming configurations.</li>
<li>"plugins/init.el": third-party packages, each with its own configuration.</li>
</ul>
<p>An example of a block you might find inside <code>plugins/init.el</code>:</p>
<pre><code class="language-elisp">;; ===
;; vterm-editor
;; Compose text in a full Emacs buffer and send it to vterm with a single keystroke.
;; Docs: https://git.andros.dev/andros/vterm-editor.el
;; ===
(use-package vterm-editor
  :ensure t
  :vc (:url "https://git.andros.dev/andros/vterm-editor.el")
  :after vterm
  :bind (:map vterm-mode-map
         ("C-c e" . vterm-editor-open)))</code></pre>
<p>Each element is isolated in its own context. Using <code>use-package</code> lets me have an isolated and organized configuration block for each package.</p>
<p>I also have other satellite files for third-party packages, such as "plugins/feeds.el" for my RSS subscriptions, "plugins/erc.el" for IRC, "plugins/mu4e-config.el" for email, "plugins/gnus.el", etc. When they grow large and specific, I split them into individual files.</p>
<p>And why break it into areas instead of a single giant <code>init.el</code>? Because I want to know where to look without stopping to think. The brain appreciates each thing having its place, especially when you come back to it after months. Besides, each file is a piece I understand fully, because I wrote it myself. Over time your configuration stops being an editor and becomes <a href="http://en.andros.dev/blog/6aba9431/emacs-is-a-fantasy-workstation/">a workstation tailored to you</a>. It is my interface with the machine, and it should be as finely tuned as possible.</p>
<p>I am not saying this is the best way, but it makes it easy for me to know where to look and where to put things. Simplicity is also a feature.</p><hr><p>Help me keep writing Every coffee gives me a push toward the next article. <a href="https://ko-fi.com/W7W02LB83">Sure, it's on me!</a></p><p>Send an email to <a href="mailto:comment+article-dc7db35a@andros.dev">comment+article-dc7db35a@andros.dev</a> to leave a comment. The subject will be ignored.</p></body></html>]]></content>
        <author>
            <name>Andros Fenollosa</name>
            <uri>http://en.andros.dev/blog/feed/en/emacs/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Chris Maiorana: Don’t go bankrupt, go local]]></title>
        <id>https://chrismaiorana.com/dont-go-bankrupt-go-local/</id>
        <link href="https://chrismaiorana.com/dont-go-bankrupt-go-local/"/>
        <updated>2026-08-27T19:32:08.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p> I saw an <a href="https://www.reddit.com/r/emacs/s/jguphKtZYm">amusing Reddit post</a> recently with an Emacs user thinking about declaring “Emacs bankruptcy” with a config file falling in the 800-900 line range. </p>
<p> Many users in the replies were happy to point out that even 10-12K lines is nothing if you understand what’s going on, and it’s well-documented. </p>
<p> This reminded me of something I’ve been experimenting with lately.  Instead of updating my config, I’m trying out directory-local variables per project. </p>
<p> Having local configurations in a project avoids the extra cogitation of worrying about conflicts with other file types or projects down the line.  I like this.  Also makes settings easier to transfer to a similar project in the future. </p>
<div>
<h2>Table of Contents</h2>
<div>
<ul>
<li><a href="https://chrismaiorana.com/category/emacs/#local">Manage multiple project settings with ease</a></li>
<li><a href="https://chrismaiorana.com/category/emacs/#eval">What’s in the dir-locals.el file?</a></li>
<li><a href="https://chrismaiorana.com/category/emacs/#organization">How to keep your settings organized</a></li>
</ul>
</div>
</div>
<div class="outline-2">
<h2>Keeping it local</h2>
<div class="outline-text-2">
<p> The bedrock of my init file has not changed much over the years.  I don’t even use a theme for very long before succumbing to the default colors.  Most of the time the editing I do in the config involves tweaking wrap and fill settings or adjusting export functions for different writing projects. </p>
<p> I’ve made use of various hooks that trigger slightly different settings for writing and programming modes.  But now I’m <i>keeping it local</i>. </p>
<p> Creating a <code>dir-locals.el</code> file right in your project’s working directory lets you set local variables and custom functions that will apply to all files in that directory tree but nowhere else in Emacs. </p>
<p> I’ve found this incredibly helpful for managing compilation settings for different writing projects with slightly different deliverable formats.  Instead of configuring multiple export options as needed, I can just edit that build file once per project. </p>
</div>
</div>
<div class="outline-2">
<h2>Evaluate on entry – juggle settings with ease</h2>
<div class="outline-text-2">
<p> The “dir-locals” file evaluates when you open that project directory.  So if you don’t visit that directory today, none of those settings will take effect. </p>
<p> I like to keep to keep my init file pretty lean anyway.  I have a habit of throwing things in as a trial and then letting them linger past usefulness. </p>
<p> The nice thing about having these directory-local functions is that they stay with the project forever, and I can tweak and adjust without having to touch my minimalist config. </p>
<p> Over the years I’ve had to oscillate needlessly between hard vs soft line wrapping.  In some cases I needed soft wrapping in org documents used for my blog, but preferred hard wrapping in plain <code>.txt</code> files.  Likewise, I might prefer to have Olivetti mode ON for a fiction project, but OFF for a technical writing project. </p>
<p> These minor problems no longer vex me.  With directory local, I can easily have one project with auto-fill-mode set at 60 characters per line and jump right to a project with visual-line-mode soft-wrapping with no sweat. </p>
</div>
</div>
<div class="outline-2">
<h2>Put the right stuff where it needs to go</h2>
<div class="outline-text-2">
<p> In most cases the big, unmovable landmasses of your editor configuration will go in your init file.  But directory-local is there for settings that should only apply to that particular project. </p>
<p> The separation might look something like this: </p>
<table>
<colgroup>
<col>
<col>
</colgroup>
<thead>
<tr>
<th>Init file</th>
<th>Directory local</th>
</tr>
</thead>
<tbody>
<tr>
<td>Theme, font, style</td>
<td>Compilation/build settings</td>
</tr>
<tr>
<td>Modeline</td>
<td>Linters</td>
</tr>
<tr>
<td>Editor defaults (auto save, backups)</td>
<td>Fill-column or wrap settings</td>
</tr>
<tr>
<td>Org agenda stuff</td>
<td>Indentation styles</td>
</tr>
</tbody>
</table>
<p> I like having this directory-local option in the back pocket so I can focus on one project at a time.  Also, I think it’s nice to have a quick and dirty way to try a certain variable on before blasting it out globally. </p>
<hr>
<p> Update on the <a href="https://chrismaiorana.com/hard-pass-beta-readers/">hacker novel beta reading call</a>: big BIG THANK YOU to everyone who reached out and offered their time and talent.  I have received some great feedback and still waiting on some more.  Then it’s off to a final draft run. </p>
<p> Here are some other nice items for you to check out: </p>
<ul>
<li>Check out my eBooks: <a href="https://chris-maiorana.kit.com/products/emacs-for-writers">Emacs For Writers</a> and <a href="https://chris-maiorana.kit.com/products/git-for-writers">Git For Writers</a>.</li>
<li>Subscribe to the <a href="https://chris-maiorana.kit.com/f9e94a7435">newsletter for occasional updates</a> on similar topics.</li>
</ul>
<p> As always, thanks for reading, see you next time. </p>
</div>
</div>
<p>The post <a href="https://chrismaiorana.com/dont-go-bankrupt-go-local/">Don’t go bankrupt, go local</a> appeared first on <a href="https://chrismaiorana.com">Chris Maiorana</a>.</p>
</body></html>]]></content>
        <author>
            <name>Chris Maiorana</name>
            <uri>https://chrismaiorana.com/category/emacs/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Charlie Holland: Hyperbole HyRolo: Search, Retrieve and Insert Records, Not Lines]]></title>
        <id>https://www.chiply.dev/post-hyperbole-hyrolo</id>
        <link href="https://www.chiply.dev/post-hyperbole-hyrolo"/>
        <updated>2026-08-27T16:44:42.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><div class="outline-2">
<h2><span class="section-number-2">1.</span> TLDR</h2>
<div class="outline-text-2">

<div class="figure">
<p><img src="https://www.chiply.dev/images/hyperbole-hyrolo-banner.webp" alt="hyperbole-hyrolo-banner.webp" width="350">
</p>
</div>

<p>
<a href="https://www.gnu.org/software/hyperbole/man/hyperbole.html#HyRolo">HyRolo</a>, the full-text search layer of <a href="https://www.gnu.org/software/hyperbole/">GNU Hyperbole</a>, is a grep-like retrieval tool for <i>records</i> in your knowledge base instead of <i>lines</i>.  Rather than retrieving single lines, it retrieves the full hierarchical record surrounding your match, descendants included.
</p>

<p>
HyRolo can search your existing knowledge bases in place (org-roam, Denote, Obsidian, Logseq, etc.), even if you use more than one of these.  You can point <code>hyrolo-file-list</code> at any container of files (directories and wildcards) containing text in any of the following formats: Org, Markdown, Koutline (Hyperbole's own outline format), or Emacs outline format.  Commands like <code>hyrolo-grep</code> will then display every matching record for your search query in a single navigable <code>*HyRolo*</code> buffer, which you can treat like an outline.
</p>

<p>
HyRolo uses full-text search to find any matching terms across your whole knowledge graph, largely eliminating the need for any graphical views of the connections.  Similarly, it eliminates or reduces the need for backlink commands since it finds all references in each search.
</p>

<p>
In this post we give a better idea of what HyRolo really is and explain why it is a uniquely powerful search and retrieval tool in Emacs.  We then demonstrate its use on my own highly fragmented, format-agnostic knowledge base.  You will see Hyperbole's philosophy in action throughout this post: HyRolo adheres to the same principles of incremental adoption and in-place data management that make its implicit buttons and HyWiki so useful.
</p>

<p>
</p><div class="youtube-container">


</div>
<p></p>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">2.</span> About&nbsp;&nbsp;&nbsp;<span class="tag"><span class="emacs">emacs</span>&nbsp;<span class="hyperbole">hyperbole</span>&nbsp;<span class="hyrolo">hyrolo</span>&nbsp;<span class="knowledgeManagement">knowledgeManagement</span></span></h2>
<div class="outline-text-2">
<p>
This is the third post in my series on Hyperbole.  The <a href="https://www.chiply.dev/post-hyperbole-hywiki">first post</a> introduced HyWiki, the zero-markup personal wiki, and the <a href="https://www.chiply.dev/post-hyperbole-implicit-buttons">second</a> covered implicit buttons, the pattern recognizers that turn your Emacs into a navigable hyperverse.
</p>

<p>
Those two posts were about <i>traversal</i>, using Hyperbole to navigate the connective tissue that already exists in your free text.  This post is about <i>retrieval</i>, the complementary capability of a knowledge/information management system.
</p>

<p>
I should contextualize my requirements for a retrieval system with an explanation of what my knowledge base looks like:
</p>

<ul>
<li><b>My notes are highly fragmented</b>.  My idea of a 'knowledge base' is more abstract than most, as I don't have a <i>single</i> directory into which I deposit my notes. Instead, I consider any information-bearing piece of text on my file system as a potential component of my overall knowledge base.  In that way, my notes are spread across local files, a synced notes directory, Dropbox directories, my blog, and the various READMEs within repositories and projects that I might install on my machine.  A knowledge management system must be able to search, edit and retrieve information from all of these sources.</li>
<li><b>The notes I write myself and the notes I read from others are ubiquitously organized in hierarchical outline format</b>, and oftentimes, the length of a line in these documents is quite small (typically &lt;80 characters) as most writers wrap their text for ergonomics.  Oftentimes, the 'thing' I'm searching for appears in an awkward place in these ~80 character chunks.  A knowledge management system needs to analyze the associated context here to retrieve the proper records.</li>
<li><b>My notes are also written in a variety of file formats</b>, including Org, Markdown, and Koutline.  Based on the above requirement, I can't control the format in which the 'records' in my notes get stored.  I mostly use Org, but most people write README documents in Markdown form, and I share files with folks who like to use Koutline.  I need a largely file-format agnostic retrieval mechanism that recognizes the hierarchical structure across all these outline formats.</li>
<li><b>I don't manually link notes together</b>, with extremely rare exceptions.  I mostly write in free text, and I avoid the bookkeeping of connecting notes that other PKMSs (like org-roam, Logseq, Obsidian, etc.) typically prescribe.  A good knowledge management system should let me <i>dynamically</i> see the associations I want.</li>
</ul>

<p>
So when I'm searching and retrieving information for a given 'thing' from this multi-format, fragmented expanse of hierarchical notes files, how can I collect everything my system knows about it into one navigable place?  This is what I use HyRolo for; it satisfies the search and retrieval requirements of my PKMS-agnostic setup: there is no notes silo, no prescribed file or data format, and no bookkeeping asked of me to enable traversal, or in HyRolo's case, retrieval.
</p>

<p>
Before HyRolo, I was using <code>consult-ripgrep</code> and <code>embark-export</code> to accomplish some form of record retrieval, but in this Consult+Embark use case the 'records' lack context.  They are too fragmented, given the <i>line-matching</i> nature of <code>grep</code>.  HyRolo goes one step further and retrieves the <i>tree context</i> around the text I'm searching for, and can retrieve from any location <i>in-place</i> that I specify in my <code>hyrolo-file-list</code>.  That's the special trick for me.  In this way, HyRolo lets me see the forest for the trees.
</p>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">3.</span> Point It at Anything&nbsp;&nbsp;&nbsp;<span class="tag"><span class="hyrolo">hyrolo</span>&nbsp;<span class="knowledgeManagement">knowledgeManagement</span></span></h2>
<div class="outline-text-2">
<p>
Like <a href="https://www.chiply.dev/post-hyperbole-hywiki">HyWiki</a>, <code>HyRolo</code> refuses to lock you into a siloed notes vault or directory.
</p>

<p>
Retrieval scope is defined by one variable, <code>hyrolo-file-list</code>, which accepts individual files, whole directories, and wildcard patterns:
</p>

<div class="org-src-container">
<pre><code><span>(</span><span>setq</span> hyrolo-file-list
      '<span>(</span><span>"~/.rolo.org"</span>             <span>;; </span><span>the classic: personal contacts
</span>        <span>"~/org/"</span>                  <span>;; </span><span>all standard Org files
</span>        <span>"~/org-roam/"</span>             <span>;; </span><span>all standard org-roam files
</span>        <span>"~/Documents/notes/"</span>      <span>;; </span><span>all standard denote files
</span>        <span>"~/vault/*.md"</span>            <span>;; </span><span>an Obsidian vault, via wildcard
</span>        <span>"~/projects/*.kotl"</span><span>)</span><span>)</span>     <span>;; </span><span>Koutliner project files</span>
</code></pre>
</div>

<p>
Although this list seems simple, I hope you recognize HyRolo's two game-changers:
</p>
<ol>
<li>Look at the middle entries: those are the <i>stock</i> locations for org-roam, Denote, and an Obsidian vault, and each of those tools ships its own <i>siloed</i> search over <i>its own directory</i>.  You can continue using those if you like, and <code>HyRolo</code> gives you <i>a single, unified retrieval scope</i> across <i>all of them</i>.  That is a major deal for me, as I not only use multiple PKMSs, but also want to include the READMEs scattered through my file system in my retrieval scope.</li>
<li><code>HyRolo</code> does <i>not</i> prescribe a file type.  It searches any hierarchically organized text it recognizes, currently the four formats: <b>Org</b>, <b>Markdown</b><sup><a href="https://www.chiply.dev/#fn.markdown-suffixes">1</a></sup>, Hyperbole's own <b>Koutline</b>, and classic <b>Emacs outline</b> files (<code>.otl</code>).</li>
</ol>

<p>
Beyond files, <code>HyRolo</code> can also fold your Google Contacts and BBDB records into the same searches<sup><a href="https://www.chiply.dev/#fn.other-sources">2</a></sup>.  There is also a standalone Python-based command-line version, <code>hyrolo.py</code>, that lets you specify both the search term and the paths to search on the command line.  It ships with Hyperbole for retrieval outside Emacs<sup><a href="https://www.chiply.dev/#fn.hyrolopy">3</a></sup>.
</p>

<p>
I want to belabor the point here because it is really important.  Your Obsidian vault, your org-roam directory, a Denote folder, and a stray project README are <i>already</i> valid <code>HyRolo</code> sources, simultaneously, with zero migration or symlinking required to unify them under a search and retrieval interface.  Where other tools ask you to migrate your notes into their containers before they will search them, <code>HyRolo</code> adds a retrieval layer <i>over</i> the containers you <i>already</i> have.  This is the same philosophy that makes HyWiki a tool for sharing and traversing knowledge, applied here to the search and retrieval use case.  There is an 'anywhere, anything' mindset to these tools that makes them unique in the knowledge management space.
</p>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">4.</span> Records as Trees, Not Lines&nbsp;&nbsp;&nbsp;<span class="tag"><span class="hyrolo">hyrolo</span>&nbsp;<span class="retrieval">retrieval</span></span></h2>
<div class="outline-text-2">
<p>
<code>HyRolo</code>'s retrieval features are unique in their treatment of what your query matches.
</p>

<p>
Think about a plain <code>grep</code>.  When <code>grep</code> (or <code>consult-grep</code>, or any of their descendants) finds a match for your query, it outputs the matching <i>line</i>, perhaps with a few lines of mechanical context via <code>-A</code> and <code>-B</code> flags.  That context is measured in lines, but lines are <i>not the unit in which you store meaning</i>.
</p>

<p>
<code>HyRolo</code> instead matches on the entity that <i>does</i> store the meaning, treating your files as sets of <i>hierarchical records</i> when it retrieves them.  As the HyRolo documentation mentions:
</p>

<blockquote>
<p>
Any search done on a [HyRolo file] scans the full text of each entry.  Whenever an entry matches, it and all of its descendant entries are retrieved.
</p>
</blockquote>

<p>
This Hyperbole manual example makes the consequence clear.  Given these entries:
</p>

<div class="org-src-container">
<pre><code>*    Company
**     Manager
***      Staffer
</code></pre>
</div>

<p>
searching for <code>Company</code> retrieves all listed employees, and searching for <code>Manager</code> turns up all Staffer entries.  Context is important, and the hierarchy of data beneath the record <i>is</i> the context.  When your query matches data in a record, it displays the record's heading, its body, and its descendants in the <code>*HyRolo*</code> match buffer with each match term highlighted.
</p>

<p>
Here's a clear distinction between the line-matching and tree-matching approaches.  With simple grep (here <code>consult-ripgrep</code> for "mitochondria" followed by <code>embark-collect</code>), we get single line matches for each term.
</p>


<div class="figure">
<p><img src="https://www.chiply.dev/images/hyrolo-fig1-records-vs-lines1.webp" alt="hyrolo-fig1-records-vs-lines1.webp">
</p>
<p><span class="figure-number">Figure 1: </span>grep returns matching lines</p>
</div>

<p>
With HyRolo, we see the full context (the tree) around each match.
</p>


<div class="figure">
<p><img src="https://www.chiply.dev/images/hyrolo-fig1-records-vs-lines2.webp" alt="hyrolo-fig1-records-vs-lines2.webp">
</p>
<p><span class="figure-number">Figure 2: </span><code>hyrolo-grep</code> returns the full hierarchical records around each match</p>
</div>

<p>
The <code>*HyRolo*</code> match buffer is itself a first-class, navigable outline.  You can jump between matches, fold and unfold entries, and get an overview of just the headings.  And because this is Hyperbole, the <a href="https://www.chiply.dev/post-hyperbole-implicit-buttons">Action Key</a> works in the <code>*HyRolo*</code> buffer.  Press <code>M-RET</code> on any retrieved entry and it is displayed for editing in its source file buffer.
</p>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">5.</span> The Search Commands&nbsp;&nbsp;&nbsp;<span class="tag"><span class="hyrolo">hyrolo</span>&nbsp;<span class="retrieval">retrieval</span></span></h2>
<div class="outline-text-2">
<p>
All of the retrieval commands live on the Rolo menu (<code>C-h h r</code>), or can be called directly:
</p>

<table>


<colgroup>
<col>

<col>

<col>
</colgroup>
<thead>
<tr>
<th>Command</th>
<th>Key</th>
<th>Finds entries containing…</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>hyrolo-fgrep</code></td>
<td><code>C-h h r s</code></td>
<td>a string, or a boolean match expression</td>
</tr>

<tr>
<td><code>hyrolo-grep</code></td>
<td><code>C-h h r r</code></td>
<td>a regular expression</td>
</tr>

<tr>
<td><code>hyrolo-word</code></td>
<td><code>C-h h r w</code></td>
<td>whole-word matches only</td>
</tr>

<tr>
<td><code>hyrolo-tags-view</code></td>
<td><code>C-h h r t</code></td>
<td>matching Org tags across your rolo files</td>
</tr>
</tbody>
</table>

<p>
The last letter of each key selects the item from the Rolo menu by its label's initial: <b>S</b>​tringFind, <b>R</b>​egexFind, <b>W</b>​ordFind, and <b>T</b>​agFind.
</p>

<p>
<code>hyrolo-word</code> matches whole words, so searching for <code>product</code> won't drown you in mentions of <code>production</code>.  And <code>hyrolo-fgrep</code> accepts full Lisp-like boolean expressions with <code>and</code>, <code>or</code>, <code>xor</code>, and <code>not</code>:
</p>

<div class="org-src-container">
<pre><code>(and postgres (not migration))
</code></pre>
</div>

<p>
The above query with <code>hyrolo-fgrep</code> retrieves every record that mentions postgres but not migration, across every file in your list.  This can be a useful query language over your entire note corpus.
</p>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">6.</span> A Knowledge Graph Without the Graph&nbsp;&nbsp;&nbsp;<span class="tag"><span class="hyrolo">hyrolo</span>&nbsp;<span class="knowledgeManagement">knowledgeManagement</span></span></h2>
<div class="outline-text-2">
<p>
This is how I use <code>HyRolo</code> most.  This is also how I think about <code>HyRolo</code> abstractly: as a more practical alternative for knowledge-graph-style tasks.
</p>

<p>
Some quick terminology first: knowledge graphs are graphs in the data-structure sense, and they show up as ball-and-stick models.  The balls are the 'nodes' and the sticks are the 'edges'.  It's a simple yet powerful model, and most PKMSs use it.
</p>

<p>
The canonical knowledge-graph <b>question</b>: <i>"show me everything connected to X"</i>.  What's the answer?
</p>

<p>
Graph-oriented PKMSs provide this answer with a backlinks pane or a graph view, but <i>only</i> if you did the up-front manual labor of hand-authoring every 'node' as a hard-coded link, inside the tool's own vault.  This answer is also quite sparse, usually just a list of node names or file names, stripped of the surrounding prose that actually defines the connection.  I think of that surrounding prose as the 'edge type', an indispensable part of the knowledge graph.
</p>

<p>
<code>hyrolo-grep</code> answers the question with more detail and fewer requirements.  Searching for a term retrieves every record it occurs in, across every file in <code>hyrolo-file-list</code>, into one <code>*HyRolo*</code> buffer, with the meaningful surrounding context <i>right there</i>.  In this way, I get every piece of the knowledge graph I need (the <code>node</code> is the ad hoc input query, the connected nodes are the records in the <code>*HyRolo*</code> buffer, and the <code>edge type</code> is the surrounding prose in the record).
</p>

<p>
You can think of the <code>*HyRolo*</code> buffer as a sophisticated (but more practical) backlinks buffer, with the added benefit that you never need to hardcode the nodes or edges, as <code>HyRolo</code> displays these on the fly based on your input query.  To emphasize, <code>HyRolo</code> can materialize "backlinks" for anything that can be matched with a regular expression.
</p>

<p>
This is the Hyperbole philosophy showing up again with <code>HyRolo</code>, and I'm glad to keep highlighting it in this series.  Other systems make you <i>author</i> relationships before you can use them, whereas Hyperbole <i>infers</i> relationships when you ask for them.  Implicit buttons infer them at the point of <i>traversal</i>, and <code>HyRolo</code> infers them at the point of <i>retrieval</i>.  With <code>HyRolo</code>, <i>mentioning</i> a term in a note <i>is</i> the linking.
</p>


<div class="figure">
<p><img src="https://www.chiply.dev/images/hyrolo-fig4-buffer.webp" alt="hyrolo-fig4-buffer.webp">
</p>
<p><span class="figure-number">Figure 3: </span>The <code>*HyRolo*</code> buffer behaves like a knowledge graph</p>
</div>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">7.</span> Yes, the Name Means <b>Rolodex</b>&nbsp;&nbsp;&nbsp;<span class="tag"><span class="hyrolo">hyrolo</span></span></h2>
<div class="outline-text-2">
<p>
The name <code>HyRolo</code> is important to address because it can be somewhat misleading.
</p>

<p>
<code>HyRolo</code> was originally written as a digital Rolodex™ for managing contact records, and it still supports that quite well.  The default <code>~/.rolo.org</code> file, automatic datestamps, alphabetized insertion, and a <code>hyrolo-mail-to</code> command all remain as features from when <code>HyRolo</code> functioned primarily as a digital contact management tool.
</p>

<p>
But it's important to emphasize here that contact management is <i>just one</i> application of <code>HyRolo</code>.  It is a generic tool.  The Hyperbole manual makes this clear:
</p>

<blockquote>
<p>
Hyperbole includes HyRolo, a complete, advanced system for convenient management of hierarchical, record-oriented information.  Most often this is used for contact management but it can quickly be adapted to most any record-oriented lookup task requiring fast retrieval.  For example, you can look up glossary entries listed in Org file headlines or legal-numbered requirements from a Koutline file.
</p>
</blockquote>

<p>
If you replace the word "contact" with "record", you get a better sense of <code>HyRolo</code>'s reach.  It is a <b>generic hierarchical record retriever</b> for any <b>outline-structured text</b>.  A record is simply a heading plus its descendants (body text and recursive subsections).
</p>

<p>
Consider what instances of data can fit into this flexible type 'record':
</p>
<ul>
<li>Notes</li>
<li>READMEs</li>
<li>Project logs</li>
<li>Architecture Decision Records (ADRs)</li>
<li>Glossary entries</li>
<li>Recipes</li>
<li>And, of course, contacts</li>
</ul>

<p>
These are all naturally hierarchical records.  The vast majority of the Emacs community uses org-mode, and so the culture of writing everything down in record form is baked deeply into the philosophy of the modern Emacs user.  You may use Markdown over org-mode, and <code>HyRolo</code> supports that as well.  In a flexible, generic way, it can retrieve records from <i>any</i> hierarchical format.
</p>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">8.</span> Consult Previews, Embark Comparisons&nbsp;&nbsp;&nbsp;<span class="tag"><span class="hyrolo">hyrolo</span>&nbsp;<span class="consult">consult</span></span></h2>
<div class="outline-text-2">
<p>
Usefully, <code>HyRolo</code> integrates with <a href="https://github.com/minad/consult">consult</a>.  <code>hyrolo-consult-grep</code> (and <code>hyrolo-consult-fgrep</code>) run your search through consult's live-updating minibuffer, so you can preview matches in place and refine your query <i>before</i> displaying a <code>*HyRolo*</code> buffer.  Although the consult buffer highlights one selected candidate, all remaining candidates and their associated records show up in the Hyperbole <code>*HyRolo*</code> match buffer when you press <code>RET</code>.
</p>


<div class="figure">
<p><img src="https://www.chiply.dev/images/hyrolo-fig3-consult.webp" alt="hyrolo-fig3-consult.webp">
</p>
<p><span class="figure-number">Figure 4: </span><code>hyrolo-consult-grep</code>: previewing matches through consult's live minibuffer before committing to a materialized <code>*HyRolo*</code> buffer</p>
</div>

<p>
For <a href="https://github.com/oantolin/embark">embark</a> users, here is the mental model I'd offer: <code>hyrolo-grep</code> feels like a <code>consult-grep</code> followed by an <code>embark-export</code>, giving you a persistent, navigable buffer of results.  The key difference is that <code>HyRolo</code> is <i>aware of hierarchical context</i>.  An embark export gives you a grep-mode buffer of matching <i>lines</i>, while <code>HyRolo</code> materializes the full records around the matches, nested structure intact, foldable as an outline, in their original formats.  This doesn't obviate <code>embark-export</code> or <code>embark-collect</code>, by the way.  I still use both frequently (for example, I use Embark for interactive 'find and replace' via <code>consult-ripgrep</code> -&gt; <code>embark-export</code> -&gt; <code>wgrep-change-to-wgrep-mode</code>).
</p>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">9.</span> Try It&nbsp;&nbsp;&nbsp;<span class="tag"><span class="hyperbole">hyperbole</span></span></h2>
<div class="outline-text-2">
<p>
The install snippet from the <a href="https://www.chiply.dev/post-hyperbole-hywiki#getting-started">first post</a> is all the setup you need.  Then tell <code>HyRolo</code> where your records live:
</p>

<div class="org-src-container">
<pre><code><span>(</span><span>setq</span> hyrolo-file-list '<span>(</span><span>"~/.rolo.org"</span> <span>"~/notes/"</span> <span>"~/org/*.org"</span><span>)</span><span>)</span>
</code></pre>
</div>

<p>
Run <code>M-x hyrolo-grep</code> (or explore the Rolo menu with <code>C-h h r ?</code>) and search for a term you know is scattered across your notes (I bet "Emacs" would turn up a ton of matching records!).  Fold the results, jump to a source with <code>M-RET</code>, then try a boolean query with <code>hyrolo-fgrep</code>.
</p>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">10.</span> Further Reading</h2>
<div class="outline-text-2">
<p>
The <a href="https://www.gnu.org/software/hyperbole/man/hyperbole.html#HyRolo">HyRolo chapter</a> of the Hyperbole manual covers the concepts, menu, search commands, keys, and settings in full.  The first two posts in this series, on <a href="https://www.chiply.dev/post-hyperbole-hywiki">HyWiki</a> and <a href="https://www.chiply.dev/post-hyperbole-implicit-buttons">implicit buttons</a>, cover the traversal side of the hyperverse that <code>HyRolo</code>'s retrieval completes.
</p>
</div>
</div>
<div>
<h2>Footnotes: </h2>
<div>

<div class="footdef"><sup><a href="https://www.chiply.dev/#fnr.markdown-suffixes">1</a></sup> <div class="footpara"><p>
HyRolo recognizes the full family of Markdown suffixes: <code>.md</code>, <code>.markdown</code>, <code>.mkd</code>, <code>.mdown</code>, <code>.mkdn</code>, and <code>.mdwn</code> (see <code>hyrolo-file-suffix-regexp</code> in <code>hyrolo.el</code>).
</p></div></div>

<div class="footdef"><sup><a href="https://www.chiply.dev/#fnr.other-sources">2</a></sup> <div class="footpara"><p>
Google Contacts are searched on each query when the <code>google-contacts</code> package is loaded (controlled by <code>hyrolo-google-contacts-flag</code>), and BBDB databases are searchable via <code>hyrolo-bbdb-grep</code> and <code>hyrolo-bbdb-fgrep</code>.
</p></div></div>

<div class="footdef"><sup><a href="https://www.chiply.dev/#fnr.hyrolopy">3</a></sup> <div class="footpara"><p>
<code>hyrolo.py</code> ships in the Hyperbole package directory and provides a command-line version of HyRolo search, useful for scripting retrieval outside of Emacs.
</p></div></div>


</div>
</div></body></html>]]></content>
        <author>
            <name>Charlie Holland</name>
            <uri>https://www.chiply.dev</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[James Cherti: Eglot for Python Development in Emacs: Integrating python-lsp-server (pylsp) with Linters and Formatters]]></title>
        <id>https://www.jamescherti.com/emacs-python-dev-using-eglot-pylsp-ruff-pylint-flake8/</id>
        <link href="https://www.jamescherti.com/emacs-python-dev-using-eglot-pylsp-ruff-pylint-flake8/"/>
        <updated>2026-08-27T16:03:21.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>Eglot provides built-in LSP support in modern Emacs, giving developers a native interface for autocompletion, linting, and formatting. When paired with <em>python-lsp-server</em> (<code>pylsp</code>), creating a Python development environment comes down to managing the LSP server configuration properly.</p>



<p>Historically, setting up this toolchain required wiring together a stack of individual utilities such as <code>flake8</code> and <code>isort</code>. Now, <code>ruff</code> stands entirely apart from that legacy ecosystem by consolidating all of those separate checks into a single, high-performance Rust binary. This article demonstrates how to configure Eglot to detect and use Ruff dynamically when it is present on your system, while retaining a clean fallback to the older stack of tools like Flake8, pycodestyle, and pydocstyle when Ruff is missing.</p>



<h2>The configuration strategy</h2>



<p>The configuration provided in this article begins by checking for the presence of <code>ruff</code> and <code>flake8</code> in your system path. Based on these checks:</p>



<ol>
<li>If Ruff is installed, the configuration relies on the <code>python-lsp-ruff</code> plugin. Ruff consolidates linting, formatting, and import sorting.</li>



<li>If Ruff is missing but Flake8 is present, Flake8 takes over linting duties alongside Pylint.</li>



<li>If neither is installed, the system falls back to the individual pylsp plugins (<code>pyflakes</code>, <code>pycodestyle</code>, <code>mccabe</code>, etc.).</li>
</ol>



<h2>Dependencies</h2>



<ul>
<li>Core: <a href="https://github.com/python-lsp/python-lsp-server">python-lsp-server</a>.</li>



<li>Primary: <a href="https://github.com/astral-sh/ruff">ruff</a>, <a href="https://github.com/python-lsp/python-lsp-ruff">python-lsp-ruff</a>, and <a href="https://github.com/pylint-dev/pylint">pylint</a>.</li>



<li>Fallback: <a href="https://github.com/PyCQA/flake8">flake8</a>, <a href="https://github.com/pycqa/flake8-docstrings">flake8-docstrings</a> (Integration of pydocstyle and flake8), <a href="https://github.com/PyCQA/isort">isort</a>, and <a href="https://github.com/chantera/python-lsp-isort">python-lsp-isort</a>.</li>
</ul>



<p>The dependencies can be installed locally <em><code>pip</code> </em>or directly via your system's package manager.</p>



<h2>The complete configuration</h2>



<p>Below is the complete Emacs Lisp code to achieve this dynamic configuration. You can place this snippet in one of your init files:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Use ruff when it is available because it is fast (written in Rust). When ruff</span>
<span class="hljs-comment">;; is not available, fall back to flake8 and its individual underlying tools.</span>
<span class="hljs-comment">;;</span>
<span class="hljs-comment">;; URL: https://www.jamescherti.com/emacs-python-dev-using-eglot-pylsp-ruff-pylint-flake8/</span>
<span class="hljs-comment">;;</span>
<span class="hljs-comment">;; To sup up:</span>
<span class="hljs-comment">;; - When ruff is available: Ruff, and Pylint.</span>
<span class="hljs-comment">;; - When Ruff is not available: Flake8, isort, and Pylint.</span>
<span class="hljs-comment">;;</span>
<span class="hljs-comment">;; Documentation:</span>
<span class="hljs-comment">;; https://github.com/python-lsp/python-lsp-server/blob/develop/CONFIGURATION.md</span>
<span class="hljs-comment">;; https://github.com/python-lsp/python-lsp-ruff</span>
<span class="hljs-comment">;; https://github.com/chantera/python-lsp-isort</span>
(<span class="hljs-name">with-eval-after-load</span> 'eglot
  (<span class="hljs-name">let*</span> ((<span class="hljs-name">has-ruff</span> (<span class="hljs-name">executable-find</span> <span class="hljs-string">"ruff"</span>))
         (<span class="hljs-name">has-flake8</span> (<span class="hljs-name">executable-find</span> <span class="hljs-string">"flake8"</span>)))
    <span class="hljs-comment">;; Target ONLY the 'pylsp key in the global configuration alist safely</span>
    (<span class="hljs-name">setf</span> (<span class="hljs-name">alist-get</span> 'pylsp (<span class="hljs-name">default-value</span> 'eglot-workspace-configuration))
          `(<span class="hljs-symbol">:pylsp</span>
            (:plugins
             (;; Plugin: https://github.com/python-lsp/python-lsp-ruff
              :ruff (;; Ruff configuration
                     :enabled ,(if has-ruff <span class="hljs-literal">t</span> :json-false)

                     :formatEnabled ,(if has-ruff <span class="hljs-literal">t</span> :json-false)

                     ;; Add 'W' (pycodestyle warnings), 'UP' (pyupgrade),
                     ;; and 'D' (pydocstyle).
                     :extendSelect [<span class="hljs-string">"W"</span> <span class="hljs-string">"UP"</span> <span class="hljs-string">"D"</span>]

                     ;; Ignore specific rules
                     ;;   D213: Multi-line docstring summary should start on
                     ;;         the second line.
                     ;;   D202: No blank lines allowed after function
                     ;;         docstring.
                     ;; :ignore [<span class="hljs-string">"D213"</span> <span class="hljs-string">"D202"</span>]
                     )

              ;; Pylint remains enabled regardless of whether Ruff
              ;; or Flake8 is active because it serves
              ;; complementary role.
              :pylint (:enabled <span class="hljs-literal">t</span>)

              ;; Flake8 is a wrapper tool that bundles pyflakes,
              ;; pycodestyle, and mccabe.
              :flake8 (:enabled ,(if (and (not has-ruff) has-flake8)
                                     <span class="hljs-literal">t</span>
                                   :json-false))

              ;; When Flake8 or Ruff runs, they execute these under
              ;; the hood. If we enable either, we must explicitly
              ;; disable the individual pylsp plugins for them,
              ;; otherwise the language server will run the exact
              ;; same checks twice and duplicate all editor
              ;; diagnostics.
              :mccabe (:enabled ,(if (or has-ruff has-flake8)
                                     :json-false
                                   <span class="hljs-literal">t</span>))
              :pyflakes (;; pyflakes catches logical errors
                         ;; (unused imports, undefined names...)
                         :enabled ,(if (or has-ruff has-flake8)
                                       :json-false
                                     <span class="hljs-literal">t</span>))

              :pycodestyle (;; pycodestyle catches style/formatting
                            ;; violations (PEP <span class="hljs-number">8</span>)
                            :enabled ,(if (or has-ruff has-flake8)
                                          :json-false
                                        <span class="hljs-literal">t</span>)

                            ;; Ignore specific rules
                            ;; :ignore [<span class="hljs-string">"W293"</span>]
                            )

              :pydocstyle (;; pydocstyle enforces PEP <span class="hljs-number">257</span> docstring
                           ;; conventions
                           :enabled ,(if (or has-ruff has-flake8)
                                         ;; Use flake8-docstrings
                                         ;; https://github.com/pycqa/flake8-docstrings
                                         :json-false
                                       <span class="hljs-literal">t</span>)

                           ;; Ignore specific rules
                           ;;   D213 Multi-line docstring summary should start on
                           ;;        the second line.
                           ;;   D202 No blank lines allowed after function
                           ;;        docstring.
                           ;; :ignore [<span class="hljs-string">"D213"</span> <span class="hljs-string">"D202"</span>]
                           )

              ;; Formatting: isort
              ;; https://github.com/chantera/python-lsp-isort
              :isort (:enabled ,(if has-ruff :json-false <span class="hljs-literal">t</span>))

              ;; Formatting: autopep8
              :autopep8 (:enabled ,(if has-ruff :json-false <span class="hljs-literal">t</span>))
              :yapf (:enabled :json-false)

              ;; Code completion
              :jedi_completion (;; jedi configuration
                                :enabled <span class="hljs-literal">t</span>

                                ;; Disable resolving documentation details eagerly
                                ;; :eager <span class="hljs-literal">t</span>

                                ;; Add class objects as a separate completion item
                                ;; :include_class_objects <span class="hljs-literal">t</span>

                                ;; Add function objects as a separate completion item
                                ;; :include_function_objects <span class="hljs-literal">t</span>

                                ;; Auto-complete methods and classes for each parameter
                                ;; :include_params <span class="hljs-literal">t</span>

                                ;; Fuzzy matching for typos/abbreviations
                                ;; :fuzzy <span class="hljs-literal">t</span>

                                ;; Modules for which labels and snippets should be cached.
                                ;; :cache_for [<span class="hljs-string">"pandas"</span>, <span class="hljs-string">"numpy"</span>, <span class="hljs-string">"tensorflow"</span>, <span class="hljs-string">"matplotlib"</span>]

                                ;; How many labels and snippets should be resolved?
                                ;; :resolve_at_most <span class="hljs-number">25</span>
                                )))))))</code></span></pre>


<p>This Eglot configuration prioritizes Ruff to handle linting and formatting when it is available in the environment. If Ruff is missing, it falls back to Flake8 and the default pylsp plugins. This keeps Emacs Eglot adaptable across different machines, guaranteeing consistent diagnostics and autocompletion without demanding a strict set of dependencies on every system you use.</p>
<div class="yarpp yarpp-related yarpp-related-rss yarpp-template-list">

<h3>Related posts:</h3><ol>
<li><a href="https://www.jamescherti.com/emacs-fix-org-mode-copy-paste-yank-bleed/">Copy-paste without Emacs org-mode or markdown-mode Formatting Bleeding Into Other Buffers</a></li>
<li><a href="https://www.jamescherti.com/emacs-persist-restore-text-scale/">persist-text-scale.el - Persist and Restore the Text Scale</a></li>
<li><a href="https://www.jamescherti.com/emacs-eglot-performance/">Configuring Emacs Eglot for Better Performance and Latency</a></li>
<li><a href="https://www.jamescherti.com/emacs-flymake-ansible-lint/">flymake-ansible-lint.el - An Emacs Emacs Flymake backend for ansible-lint</a></li>
<li><a href="https://www.jamescherti.com/emacs-flymake-bashate-bash/">flymake-bashate.el - An Emacs Flymake backend for bashate</a></li>
<li><a href="https://www.jamescherti.com/emacs-compile-angel-byte-native-compile/">The compile-angel Emacs package: Byte-compile and Native-compile Emacs Lisp libraries Automatically</a></li>
<li><a href="https://www.jamescherti.com/emacs-ultisnips-mode-edit-snippets-files/">ultisnips-mode.el - An Emacs major mode for editing Ultisnips snippet files (*.snippets files)</a></li>
</ol>
</div>
</body></html>]]></content>
        <author>
            <name>James Cherti</name>
            <uri>https://www.jamescherti.com</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Irreal: Tweaking Emacs Scrolling Behavior]]></title>
        <id>https://irreal.org/blog/?p=14042</id>
        <link href="https://irreal.org/blog/?p=14042"/>
        <updated>2026-08-27T14:15:48.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
For me, one of the most difficult Emacs behaviors—or at least “normal” Emacs behaviors—to understand is scrolling. The default behavior is so unintuitive and jarring that it kept me from embracing Emacs for a long time. It was only after I took the plunge that I discovered that that behavior, like everything else in Emacs, is configurable.
</p>
<p>
That was almost 20 years ago when I was just starting and knew next to nothing about Emacs configuration but I was able find the solution on the Internet. Here’s what I’ve had in my <code>init.el</code> almost from the beginning:
</p>
<div class="org-src-container">
<pre><code>(<span>setq</span>    <span>;</span><span>set reasonable scrolling
</span> scroll-margin 0
 scroll-conservatively 100000
 scroll-preserve-screen-position 1)
</code></pre>
</div>
<p>
Back then, I had no idea what any of it meant, only that it made Emacs scrolling rational by my standards. It’s been sitting there undisturbed ever since.
</p>
<p>
Now James Cherti has <a href="https://www.jamescherti.com/emacs-scrolling-better-performance-usability/">a really excellent post that expains all the fine points of configuring scrolling</a>. He explains the above lines, which most people probably have in their configuration. If you want Emacs scrolling to behave as it does in every other application, you can leave them as is but you can tweak the behavior a bit if you like. Cherti has all the details.
</p>
<p>
But wait. There’s more. There are similar settings for horizontal scrolling that you might like. I’ve never had any problem with horizontal scrolling—probably because I’ve always wrapped lines in one way or another—but they’re there if you need them.
</p>
<p>
There are some other tweaks that you can apply to delay fontification during scrolling to speed things up a bit. Again, this is not something that I use but if you deal with large files, you may find it useful.
</p>
<p>
In Emacs 29 and later you can set pixel-precise scrolling to make scrolling over images more pleasant. There are a few other tweaks in Cherti’s post so you should definitely take a look at it. There’s a lot of good content and even if you use only a bit of it, you will find his post worthwhile.</p>
</body></html>]]></content>
        <author>
            <name>Irreal</name>
            <uri>https://irreal.org/blog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[TAONAW - Emacs and Org Mode: ]]></title>
        <id>https://taonaw.com/2026/08/27/gcc-drive-error-on-macos.html</id>
        <link href="https://taonaw.com/2026/08/27/gcc-drive-error-on-macos.html"/>
        <updated>2026-08-27T12:51:50.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>This is now fixed: GCC drive error on macOS: This seems to show up with almost every new Emacs package I’ve been trying recently on my Mac. I remember trying to dig into it, and I went in circles trying to install something from Homebrew. Has anyone fixed this? I’m on GNU Emacs plus 30.2.</p>
<img src="https://cdn.uploads.micro.blog/96826/2026/2026-08-27-08-47-30.png" alt="A terminal window displays repeated error messages indicating a failure to invoke the GCC driver while compiling with native-compiler in Emacs.">
</body></html>]]></content>
        <author>
            <name>TAONAW - Emacs and Org Mode</name>
            <uri>https://taonaw.com/categories/emacs-org-mode/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Rahul Juliato: An unofficial guide to markdown-ts-mode on Emacs 31]]></title>
        <id>https://rahuljuliato.com/posts/markdown-ts-mode-emacs-31</id>
        <link href="https://rahuljuliato.com/posts/markdown-ts-mode-emacs-31"/>
        <updated>2026-08-26T23:00:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><h2>Intro</h2>
<p>So, <code>Emacs 31</code> has been released, and a lot of shiny new stuff is
there, ready for us to play with.</p>
<p>You probably heard of this new <code>markdown-ts-mode</code> and decided to check
it out. And guess what? On Emacs version <code>31</code>, this is marked as an
experimental mode. What does this mean? Should you use it or not? Is
this ready? Is this just a sketch of a mode?</p>
<p>Treat this post as a quick guide to getting this mode up and running
and helping yourself find answers to these questions.</p>
<h2>Where is it in terms of features?</h2>
<p>This is an experimental mode, right? You need to opt in, so probably
not everything will work flawlessly yet, and it needs more testing and
feedback.</p>
<p>That said, don't let this title mislead you. This does not mean the
mode is premature in terms of features. As you will see, this is a
very feature-rich mode. This mode already covers all of the
<a href="https://commonmark.org/">https://commonmark.org/</a> spec, as well as
most of
<a href="https://github.github.com/gfm/">https://github.github.com/gfm/</a>, with
some extras like code blocks even for non-<code>ts-mode</code>s, like <code>elisp</code>,
table of contents utilities, and interfaces with external converters,
such as <code>pandoc</code> and <code>gfm</code>.</p>
<p>Before deep diving into it yourself, you may need some help simply
turning this mode on. Tree-sitter is tricky. It might even be your
first time with tree-sitter, so a quick "install guide" is on our
agenda.</p>
<h2>Where is it? Do I need to install the mode?</h2>
<p>Experimental means Emacs does not enable the mode by default so it is
not there waiting for you to simply open a <code>.md</code> file or call it with
<code>M-x markdown-ts-mode RET</code>.  You need to load this library.</p>
<p>As always on Emacs, there's more than one way of doing everything, I
am a big fan of <code>use-package</code> so I tend to use it to organize my
<code>init</code> file.  Here is my suggested initial setup:</p>
<div class="remark-highlight"><pre><code class="language-elisp"><span class="token punctuation">(</span><span class="token keyword">use-package</span> markdown-ts-mode
  <span class="token lisp-property property">:ensure</span> <span class="token boolean">nil</span>
  <span class="token lisp-property property">:mode</span> <span class="token punctuation">(</span><span class="token string">"\\.md\\'"</span> <span class="token string">"\\.mdx\\'"</span> <span class="token string">"\\.markdown\\'"</span><span class="token punctuation">)</span>
  <span class="token lisp-property property">:config</span>
  <span class="token punctuation">(</span><span class="token keyword">require</span> <span class="token quoted-symbol variable symbol">'markdown-ts-mode-x</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</code></pre></div>
<p>Or if you keep <code>use-package</code> out of your tool belt:</p>
<div class="remark-highlight"><pre><code class="language-elisp"><span class="token punctuation">(</span><span class="token car">autoload</span> <span class="token quoted-symbol variable symbol">'markdown-ts-mode</span> <span class="token string">"markdown-ts-mode"</span> <span class="token boolean">nil</span> <span class="token boolean">t</span><span class="token punctuation">)</span>

<span class="token punctuation">(</span><span class="token car">dolist</span> <span class="token punctuation">(</span><span class="token car">re</span> <span class="token punctuation">'(</span><span class="token string">"\\.md\\'"</span> <span class="token string">"\\.mdx\\'"</span> <span class="token string">"\\.markdown\\'"</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">(</span><span class="token car">add-to-list</span> <span class="token quoted-symbol variable symbol">'auto-mode-alist</span> <span class="token punctuation">(</span><span class="token keyword">cons</span> re <span class="token quoted-symbol variable symbol">'markdown-ts-mode</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>

<span class="token punctuation">(</span><span class="token car">with-eval-after-load</span> <span class="token quoted-symbol variable symbol">'markdown-ts-mode</span>
  <span class="token punctuation">(</span><span class="token keyword">require</span> <span class="token quoted-symbol variable symbol">'markdown-ts-mode-x</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
</code></pre></div>
<p>Now both the mode and the <code>x</code> (nice extra goodies) libraries are
loaded, and you can simply visit your Markdown files using it.</p>
<p>If you want to experiment with it without touching your own
configuration, do the following:</p>
<ol>
<li>Save the above content in a file like <code>testing.el</code>.</li>
<li>Call <code>emacs</code> with <code>emacs -Q --load 'testing.el'</code>.</li>
</ol>
<p>And there you have it, a bare Emacs session with your testing ground
set up. This is what I will use for the rest of this guide.</p>
<blockquote>
<p><strong>IMPORTANT</strong>: there's <em>NO NEED</em> to download or add this
package to your package manager. The (now very old and archived)
<a href="https://github.com/LionyxML/markdown-ts-mode">MELPA Repository</a>
will refuse to install on Emacs version 31 onward and is very, very
poor in terms of features. If you are using this, you're not using
the new <strong>built-in</strong> <code>markdown-ts-mode</code>. Right? Let's continue.</p>
</blockquote>
<h2>Opening our first markdown file</h2>
<p>In order for you to "see what I see", we need some pictures. If it is
the first time you're using a tree-sitter-based mode, let me warn you:
although tree-sitter is wonderful, fast, and feature-rich, it comes
with its own set of tasks to complete and perhaps debugging skills if
it needs help. I will try to cover some here; I will forget others for
sure.</p>
<p>For this guide, I will be using this <a href="https://github.com/LionyxML/markdown-ts-mode-lab/blob/main/test.md">test
file</a>.</p>
<p>The repository where it is hosted is our laboratory. No code lives
there, remember, all code is in Emacs itself.</p>
<p>Now go ahead and open the <code>test.md</code> file.</p>
<blockquote>
<p><strong>IMPORTANT</strong>: At this point, many things can happen. If you have
the grammar for <code>markdown</code> installed in your system, the file is
already opened. You could, though, be prompted, as I am here, with
this:</p>
</blockquote>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-01.png" alt="emacs_markdown_31_demo step 01"></p>
<p>It means Emacs hasn't found a grammar for <code>markdown</code> in my system, in
this case in <code>~/.emacs.d/tree-sitter/</code> (which is the default when I
start Emacs with <code>emacs -Q ...</code>). Emacs will offer to install it,
which means downloading and compiling it from a repository already
defined in <code>markdown-ts-mode</code>'s source code. Let's install it with
<code>y</code>. Emacs will clone the grammar repository, compile it, and continue
to the second grammar. Yes, <code>markdown</code> uses two grammars: the main one
and one for <em>inline</em> parsing. I will allow Emacs to install the second
one with <code>y</code>.</p>
<p>Success!</p>
<p>What you should be seeing:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-02.png" alt="emacs_markdown_31_demo step 02"></p>
<p>If not, here is what you should check if something went wrong:</p>
<ol>
<li>
<p>Is Emacs compiled with the tree-sitter flag? Use <code>M-: (featurep 'treesit) RET</code> and check if it returns <code>t</code>.</p>
</li>
<li>
<p>Do you have the tooling used for "compiling" grammars, like <code>make</code>,
<code>gcc</code>, and others?</p>
</li>
<li>
<p>Tree-sitter needs a package in your distro, usually named
<code>tree-sitter-cli</code> which provides a <code>tree-sitter</code> binary, you can
check you have it with <code>tree-sitter --version</code>.</p>
</li>
</ol>
<p>This is a common headache for all <code>tree-sitter</code> modes. Many people
like <strong>NOT</strong> to compile their own grammars, but instead use some
compiled file from a place they trust, like their own distro
repository, or packages with hundreds of pre-compiled grammars. I will
not dive into it; there are many ways of acquiring grammars, and I
will stick with "build it yourself" for this guide.</p>
<p>See, I kind of tricked you there. I told you that you should be seeing
that, but actually, the "do you see what I see" should look like this:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-03.png" alt="emacs_markdown_31_demo step 03"></p>
<p>We provide the full file in
<a href="https://github.com/LionyxML/markdown-ts-mode-lab/blob/f3368384a92464607ddb189c580a92d1a9fd2a89/demo/demo-default-theme-raw.md">here</a>,
with several default themes so you can compare whether your setup is
complete.</p>
<p>So, what happened?</p>
<p>This is part of the reason <code>markdown-ts-mode</code> is <strong>very special</strong>.</p>
<p>This mode can work not only with <code>markdown</code>, but with all other
<code>-ts-mode</code>s available! Keep this in mind; we will talk about code
blocks in a while. For now, we need to understand a few things.</p>
<p>In your <code>test.md</code> file, we have a special header. It is very common to
have <code>toml</code> or <code>yaml</code> as headers of <code>markdown</code> files.</p>
<p>This little guy here:</p>
<div class="remark-highlight"><pre><code class="language-yaml"><span class="token punctuation">---</span>
<span class="token key atrule">title</span><span class="token punctuation">:</span> The Official 'markdown<span class="token punctuation">-</span>ts<span class="token punctuation">-</span>mode.el' Feature Test File
<span class="token key atrule">author</span><span class="token punctuation">:</span> Rahul Martim Juliato
<span class="token key atrule">date</span><span class="token punctuation">:</span> <span class="token datetime number">2026-03-18</span>
<span class="token key atrule">version</span><span class="token punctuation">:</span> 0.1.0
<span class="token key atrule">parsers needed</span><span class="token punctuation">:</span> markdown<span class="token punctuation">,</span> markdown<span class="token punctuation">-</span>inline<span class="token punctuation">,</span> yaml<span class="token punctuation">,</span> toml<span class="token punctuation">,</span> html<span class="token punctuation">,</span> c<span class="token punctuation">,</span> javascript<span class="token punctuation">,</span> python<span class="token punctuation">,</span> ruby<span class="token punctuation">,</span> rust
<span class="token punctuation">---</span>
</code></pre></div>
<p>Needs something else to <em>fontify</em> (aka be painted with colors by
Emacs).  Can you figure out what is missing? If your answer is "we
need a grammar for YAML!", kudos!</p>
<p>Whenever something does not fontify correctly in <code>-ts-mode</code>s, you're
probably missing a grammar. And as <code>markdown-ts-mode</code> is made to work
with <strong>all available ts-modes</strong>, this is no exception.</p>
<p>Let's install our <code>yaml</code> grammar with our trusty <code>M-x treesit-install-language-grammar RET yaml</code>.</p>
<p>You might see now what I am seeing:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-04.png" alt="emacs_markdown_31_demo step 04"></p>
<p>Let's agree to it with <code>y</code>. Hmm, it looks like this time, something
went wrong with <code>yaml-ts-mode</code> trying to register its preferred
grammar with <code>treesit-install</code>, as there are no suggestions. We could
provide it manually. But let's check something first. Taking a look at
<code>yaml-ts-mode.el</code>, we can check which grammar it expects in its source
code:</p>
<div class="remark-highlight"><pre><code class="language-elisp"><span class="token comment">;; from yaml-ts-mode.el</span>
<span class="token punctuation">(</span><span class="token car">add-to-list</span>
 <span class="token quoted-symbol variable symbol">'treesit-language-source-alist</span>
 <span class="token punctuation">'(</span><span class="token car">yaml</span> <span class="token string">"https://github.com/tree-sitter-grammars/tree-sitter-yaml"</span>
		<span class="token lisp-property property">:commit</span> <span class="token string">"b733d3f5f5005890f324333dd57e1f0badec5c87"</span><span class="token punctuation">)</span>
 <span class="token boolean">t</span><span class="token punctuation">)</span>
</code></pre></div>
<p>Awesome! Let's simply evaluate that block and try to install the
grammar again. Or manually provide the source
<code>https://github.com/tree-sitter-grammars/tree-sitter-yaml</code> to our
already-started interactive session, as I did this time:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-05.png" alt="emacs_markdown_31_demo step 05"></p>
<p>We then keep going with the defaults with <code>RET RET RET...</code> until the
library is installed.</p>
<p>After that, reload <code>markdown-ts-mode</code>, or use <code>C-x x g</code>, or re-open
the file you're visiting.</p>
<p>What we did here by visiting the source code is pretty rare, and most
<code>-ts-mode</code>s will automatically suggest the repository from which they
are going to compile. It was nice that this happened, so I can show
you what to do.</p>
<p>Now what? We need to do the same <code>M-x treesit-install-language-grammar</code> for every block without
<em>fontification</em> that we encounter. If you'd like, for our test file we
could use <code>C-x x f</code> to force <em>fontification</em> and be prompted for every
missing grammar used by this file.</p>
<p>By now, you should see the entire document <em>fontified</em> as in
<a href="https://github.com/LionyxML/markdown-ts-mode-lab/blob/main/demo/demo-default-theme-raw.md">here</a>. Same
as previous image:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-03.png" alt="emacs_markdown_31_demo step 03"></p>
<h2>A note on grammars</h2>
<p><strong>A <code>-ts-mode</code> is only as good as the tree-sitter grammar behind it</strong>.
This means every <code>-ts-mode</code> needs to constantly keep up with
improvements to the <code>grammar</code>, which is shared by any editor or
program wanting to use <code>tree-sitter</code> to parse the language.</p>
<p>This also means we are, at some point, <em>dependent</em> on the grammar for
certain constraints and features. Almost all <code>-ts-mode</code> code in Emacs
is filled with notes on limitations and the reasoning behind why and
how something obscure is treated the way it is.</p>
<p>Emacs mode authors and maintainers always try to suggest the grammar
and the SHA commit the <code>ts-mode</code> is prepared to use, either in
comments or in the code inside the mode, which is the same as you saw
for the <code>yaml</code> suggestion. Part of maintaining <code>ts-mode</code>s is keeping
up with newer grammar version changes. We try our best to keep it
updated with the latest versions, but the one we tested against and
that should work as expected is the one in the source file of the
mode.</p>
<p>This is why I think compiling it yourself interactively with Emacs is
the best possible way to guarantee a nice experience.</p>
<p>Specifically for <code>markdown-ts-mode</code>, we're using the grammars provided
by <a href="https://github.com/tree-sitter-grammars/tree-sitter-markdown">https://github.com/tree-sitter-grammars/tree-sitter-markdown</a>, as
this is the most complete, maintained, and broadly adopted one, both
by code editors and programs in general. This doesn't mean it is free
of bugs or limitations. Again, we do our best to work around these
limitations and even contribute issues to the grammar and to the core
tree-sitter library.</p>
<h2>I can finally open a markdown file!</h2>
<p>Congrats! Now what? How often do I need to do all of this? Only once,
the first time you use a <code>-ts-mode</code>, or never if you already have
grammars installed by some other method.</p>
<p>Now let's see what <code>markdown-ts-mode</code> already provides.</p>
<h2>A quick look at <code>markdown-ts-mode</code> features</h2>
<p>We (BTW, this mode is authored by me and Stéphane Marks) provided an
<code>easy-menu</code> feature for quick discoverability of functionalities.</p>
<p>You can access it by clicking on <code>Markdown</code> in the <code>mode-line</code>, or, if
you have <code>menu-bar-mode</code> enabled, on the menu bar, or even <code>Ctrl + Right click</code> (whatever Emacs maps your OS input to) on a buffer using
<code>markdown-ts-mode</code>.</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-06.png" alt="emacs_markdown_31_demo step 06"></p>
<p>This is actually this guide's <em>TL;DR</em>, if you want to stop now and
explore it yourself (spoilers ahead).</p>
<h2>Editing</h2>
<p>The fastest way to learn the mode is to type a little of everything.
Below is a speed run: what you write, what key does it for you.</p>
<h3>Marks (emphasis)</h3>
<p>Markdown is plain text, so you can always type the markers yourself:</p>
<table>
<thead>
<tr>
<th>When you want</th>
<th>You write</th>
</tr>
</thead>
<tbody>
<tr>
<td>bold</td>
<td><code>**bold**</code></td>
</tr>
<tr>
<td>bold, alt</td>
<td><code>__bold__</code></td>
</tr>
<tr>
<td>italic</td>
<td><code>*italic*</code></td>
</tr>
<tr>
<td>italic, alt</td>
<td><code>_italic_</code></td>
</tr>
<tr>
<td>bold + italic</td>
<td><code>***both***</code></td>
</tr>
<tr>
<td>strikethrough</td>
<td><code>~~gone~~</code></td>
</tr>
<tr>
<td>inline code</td>
<td><code>`code`</code></td>
</tr>
</tbody>
</table>
<p>Or let the mode do it: <code>C-c C-x C-f</code> (<code>markdown-ts-emphasize</code>) then a
single key:</p>
<ul>
<li><code>b</code> bold, <code>B</code> bold with underscores</li>
<li><code>i</code> italic, <code>I</code> italic with underscores</li>
<li><code>a</code> bold + italic</li>
<li><code>s</code> strikethrough</li>
<li><code>c</code> inline code</li>
<li><code>SPC</code> remove emphasis at point</li>
</ul>
<p>If a region is active, the formatting wraps the region. With no
region, it wraps the word at point, or inserts the pair and drops
point in the middle.</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-07.png" alt="emacs_markdown_31_demo step 07"></p>
<p>Tip: <code>C-c C-x RET</code> (<code>markdown-ts-toggle-hide-markup</code>) hides the
markers themselves, so <code>**bold**</code> shows as <strong>bold</strong>. Very nice for
reading while editing, like default <code>org-mode</code>.</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-08.png" alt="emacs_markdown_31_demo step 08"></p>
<p>Another tip: <code>M-q</code> fills correctly even inside lists and quotes.</p>
<h3>Headings</h3>
<p>Type them: <code>#</code>, <code>##</code>, ... up to <code>######</code>. Setext headings (<code>===</code> and
<code>---</code> underlines) are recognized, too.</p>
<p>Promote and demote without retyping the hashes:</p>
<ul>
<li><code>M-&lt;left&gt;</code> promote (<code>markdown-ts-promote</code>)</li>
<li><code>M-&lt;right&gt;</code> demote (<code>markdown-ts-demote</code>)</li>
</ul>
<p>And move a whole section, body and children included:</p>
<ul>
<li><code>M-&lt;up&gt;</code> (<code>markdown-ts-move-subtree-up</code>)</li>
<li><code>M-&lt;down&gt;</code> (<code>markdown-ts-move-subtree-down</code>)</li>
</ul>
<p><code>TAB</code> on a heading cycles its visibility (outline folding). The mode
is an <code>outline-minor-mode</code> citizen, so folding just works. <code>S-TAB</code> on
a heading will cycle the visibility of all headings.</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-09.png" alt="emacs_markdown_31_demo step 09"></p>
<blockquote>
<p><strong>IMPORTANT</strong>: By now, you can see this mode tries, when possible, to
draw parallels with <code>org-mode</code>, so Emacs users used to it can have
fewer problems adapting to <code>markdown</code>. If these bindings don't suit
you, everything can be customized.</p>
</blockquote>
<h3>Listings (lists and checkboxes)</h3>
<p>Type <code>- item</code>, <code>+ item</code>, <code>* item</code> or <code>1. item</code>.</p>
<ul>
<li><code>M-RET</code> new list item (<code>markdown-ts-insert-list-item</code>)</li>
<li><code>RET</code> is smart: <code>markdown-ts-newline</code> continues the list for you</li>
<li><code>M-&lt;left&gt;</code> / <code>M-&lt;right&gt;</code> promote/demote the item</li>
<li><code>C-c C-r</code> renumber an ordered list (<code>markdown-ts-renumber-list</code>)</li>
<li><code>C-c C-c</code> toggle a task checkbox (<code>markdown-ts-toggle-checkbox</code>)</li>
<li><code>M-q</code> fills correctly inside an item</li>
</ul>
<p>Task lists are the GFM ones:</p>
<div class="remark-highlight"><pre><code class="language-markdown"><span class="token list punctuation">-</span> [ ] not done
<span class="token list punctuation">-</span> [x] done
</code></pre></div>
<p>Raw mode:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-10.png" alt="emacs_markdown_31_demo step 10"></p>
<p>With markup hidden:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-11.png" alt="emacs_markdown_31_demo step 11"></p>
<p>Note the bullets and boxes you see if you toggled <code>C-c C-x RET</code> are
display only.  The buffer still holds <code>-</code> and <code>[x]</code>. See
<code>markdown-ts-unordered-list-marker</code>, <code>markdown-ts-checked-checkbox</code>
and <code>markdown-ts-unchecked-checkbox</code>.</p>
<h3>Blocks</h3>
<p><code>C-c C-,</code> (<code>markdown-ts-insert-structure</code>) then one key:</p>
<ul>
<li><code>`</code> fenced code block, prompts for the language</li>
<li><code>~</code> tilde fenced code block</li>
<li><code>q</code> block quote</li>
<li><code>d</code> divider (thematic break)</li>
<li><code>t</code> table</li>
</ul>
<p>If a region is active, it wraps the region instead of inserting an
empty block.</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-12.png" alt="emacs_markdown_31_demo step 12"></p>
<p>With markup hidden:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-13.png" alt="emacs_markdown_31_demo step 13"></p>
<h3>Code blocks</h3>
<p>This is the party trick. A fenced block tagged with a language is
fontified by that language's own mode:</p>
<div class="remark-highlight"><pre><code class="language-markdown"><span class="token code"><span class="token punctuation">```</span><span class="token code-language">python</span>
<span class="token code-block language-python"><span class="token keyword">def</span> <span class="token function">hello</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
	<span class="token keyword">return</span> <span class="token string">"world"</span></span>
<span class="token punctuation">```</span></span>
</code></pre></div>
<p>Missing colors typically means a missing grammar, same story as the
<code>yaml</code> header earlier.</p>
<p>Better than colors: put point inside the block and you are in
<code>markdown-ts-code-block-in-context-mode</code> (lighter <code> [code]</code> in the
mode-line).  Inside it:</p>
<ul>
<li><code>TAB</code> indents like the language does</li>
<li><code>RET</code> newline and indent like the language does</li>
<li><code>M-q</code> fills like the language does</li>
<li><code>M-.</code> jumps to definition via <code>xref</code></li>
</ul>
<p>Move to the next/previous blocks with <code>C-c C-v n</code> and <code>C-c C-v p</code>.</p>
<p>Non tree-sitter modes work too, <code>elisp</code> included. Knobs:
<code>markdown-ts-code-block-modes</code>, <code>markdown-ts-default-code-block-mode</code>,
<code>markdown-ts-fontify-code-blocks-natively</code>.</p>
<p>An example raw:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-14.png" alt="emacs_markdown_31_demo step 14"></p>
<p>With markup hidden:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-15.png" alt="emacs_markdown_31_demo step 15"></p>
<h3>Tables</h3>
<p>Insert one with <code>C-c C-,</code> <code>t</code> or <code>M-x markdown-ts-table-insert-table</code>,
which asks you to specify the number of rows and columns to insert.</p>
<div class="remark-highlight"><pre><code class="language-markdown"><span class="token table"><span class="token table-header-row"><span class="token punctuation">|</span><span class="token table-header important"> Column 1 </span><span class="token punctuation">|</span><span class="token table-header important"> Column 2 </span><span class="token punctuation">|</span>
</span><span class="token table-line"><span class="token punctuation">|</span><span class="token punctuation">----------</span><span class="token punctuation">|</span><span class="token punctuation">:---------</span><span class="token punctuation">|</span>
</span><span class="token table-data-rows"><span class="token punctuation">|</span><span class="token table-data"> a        </span><span class="token punctuation">|</span><span class="token table-data">        1 </span><span class="token punctuation">|</span></span></span>
</code></pre></div>
<p>Inside a table you are in <code>markdown-ts-in-table-mode</code> (lighter <code> [table]</code>) and the keys change:</p>
<ul>
<li><code>TAB</code> / <code>S-TAB</code> next / previous cell (also formats your table)</li>
<li><code>RET</code> / <code>S-RET</code> next / previous row</li>
<li><code>M-RET</code> insert row below</li>
<li><code>M-&lt;up&gt;</code> / <code>M-&lt;down&gt;</code> move row</li>
<li><code>M-&lt;left&gt;</code> / <code>M-&lt;right&gt;</code> move column</li>
<li><code>M-S-&lt;up&gt;</code> insert row above, <code>M-S-&lt;down&gt;</code> delete row</li>
<li><code>M-S-&lt;right&gt;</code> insert column left, <code>M-S-&lt;left&gt;</code> delete column</li>
<li><code>C-c C-c</code> align the whole table</li>
<li><code>C-c C-t a</code> set column alignment (left, center, right)</li>
<li><code>C-c C-t t</code> transpose the table</li>
</ul>
<p>Plus, from the menu: clone rows and columns, CSV/TSV import of a
region and CSV/TSV export of the table.</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-16.png" alt="emacs_markdown_31_demo step 16"></p>
<blockquote>
<p><strong>NOTE:</strong> There are some limitations when working with tables at the
moment, mostly due to how the grammar parses them, so you may bump
into unfontified stuff while typing. All valid tables according to
the GFM spec should be good to use, though.</p>
</blockquote>
<h3>Links and images</h3>
<p>Links are the usual <code>[text](url)</code> and <code>[text][ref]</code>. Fragment links
like <code>[intro](#intro)</code> are clickable and jump to the heading in the
buffer, using GitHub style slugs by default.</p>
<p>Images render inline. <code>C-c C-x C-v</code> toggles them
(<code>markdown-ts-toggle-inline-images</code>). See
<code>markdown-ts-image-max-width</code> and
<code>markdown-ts-display-remote-inline-images</code> for how big and whether
remote URLs are fetched.</p>
<p>Markdown:
<img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-17.png" alt="emacs_markdown_31_demo step 17"></p>
<p>After <code>C-c C-x C-v</code>:
<img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-18.png" alt="emacs_markdown_31_demo step 18"></p>
<p>After <code>C-c C-x RET</code>:
<img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-19.png" alt="emacs_markdown_31_demo step 19"></p>
<h2>Moving around</h2>
<ul>
<li><code>TAB</code> cycle folding at point</li>
<li><code>C-c C-n</code> / <code>C-c C-p</code> next / previous heading</li>
<li><code>C-c C-u</code> up to parent heading</li>
<li><code>C-c C-f</code> / <code>C-c C-b</code> next / previous heading, same level</li>
<li><code>M-x imenu</code> jump to any heading or named code block by completion</li>
<li><code>C-c C-v n</code> / <code>C-c C-v p</code> next / previous code block</li>
</ul>
<p><code>markdown-ts-default-folding</code> decides how a file opens: everything
shown, or folded.</p>
<h3>markdown-ts-view-mode</h3>
<p><code>M-x markdown-ts-view-mode</code> read-only mode with a single key
navigation: <code>n</code>, <code>p</code>, <code>u</code>, <code>f</code>, <code>b</code>, <code>TAB</code>. Good for reading a README
without fear of typing into it.</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-20.png" alt="emacs_markdown_31_demo step 20"></p>
<h2>Extras</h2>
<p>Everything below lives in <code>markdown-ts-mode-x.el</code>, which is why we
loaded it back in the setup.</p>
<h3>TOC</h3>
<p>A table of contents is delimited by HTML comments, so it survives
rendering anywhere:</p>
<div class="remark-highlight"><pre><code class="language-markdown"><span class="token comment">&lt;!-- markdown-ts-toc: --&gt;</span>
<span class="token comment">&lt;!-- markdown-ts-toc-end: --&gt;</span>
</code></pre></div>
<ul>
<li><code>M-x markdown-ts-toc-insert-template</code> inserts those markers, basic
or complete (the complete one lists every parameter with its
default)</li>
<li><code>M-x markdown-ts-toc-generate</code> fills them in, and refills on every
call</li>
<li><code>M-x markdown-ts-toc-clear</code> empties,
<code>markdown-ts-toc-clear-and-remove</code> also removes the markers</li>
<li><code>M-x markdown-ts-toc-update-before-save-mode</code> regenerates on save</li>
</ul>
<p>Parameters go inline in the opening comment: <code>min-depth</code>, <code>max-depth</code>,
<code>candidates</code>, <code>from</code>, <code>style</code>, <code>indent</code>, <code>no-link</code>, <code>relative-depth</code>,
<code>ignore</code>. A buffer can hold more than one table with different
parameters. Candidates are not only headings, list items, setext
headers and named code blocks can feed a table too.</p>
<p>Raw:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-21.png" alt="emacs_markdown_31_demo step 21"></p>
<p>With markup hidden:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-22.png" alt="emacs_markdown_31_demo step 22"></p>
<h3>Exporting</h3>
<p><code>M-x markdown-ts-convert</code> converts the buffer,
<code>markdown-ts-convert-file</code> a file. You get asked for the format and
the converter, unless you set
<code>markdown-ts-default-converter</code>. Supported out of the box:</p>
<ul>
<li>PDF via <code>pandoc</code></li>
<li>HTML via <code>pandoc</code>, <code>cmark</code>, <code>cmark-gfm</code>, <code>markdown</code>, <code>markdown.pl</code></li>
</ul>
<p>With a prefix argument the result is displayed, by default with <code>eww</code>.
See <code>markdown-ts-convert-display-function</code> to open in a browser
instead. That is your somewhat 'live' preview. Converting is not (yet)
automatically when you make changes, maybe in the future.</p>
<p>Example using <code>eww</code>, split  manually made for this demo:</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-23.png" alt="emacs_markdown_31_demo step 23"></p>
<h3>Spec at hand</h3>
<p><code>M-x markdown-ts-browse-commonmark-spec</code> and <code>M-x markdown-ts-browse-gfm-spec</code> open the specs, for when you need to
settle an argument.</p>
<h2>Experiment with <code>eglot</code> and <code>eldoc</code></h2>
<p>This is still experimental within the experimental, so don't blame
<code>eglot</code>'s author if something goes wrong. Send a bug report to
<code>markdown-ts-mode</code> instead.</p>
<p>If you set this:</p>
<div class="remark-highlight"><pre><code class="language-elisp"><span class="token punctuation">(</span><span class="token car">setopt</span> eglot-documentation-renderer <span class="token quoted-symbol variable symbol">#'markdown-ts-view-mode</span><span class="token punctuation">)</span>
</code></pre></div>
<p>Eglot will try to render documentation (usually Markdown provided by
the LSP server) using <code>markdown-ts-mode</code>.</p>
<p><img src="https://rahuljuliato.com/cdn-cgi/image/format=auto,width=1280,quality=75/assets/blog/posts/markdown-ts-mode-31-24.png" alt="emacs_markdown_31_demo step 24"></p>
<p>Again, we are still shaving off some rough edges here, and results may
vary. Please do help us test this, though.</p>
<h2>Play with options</h2>
<p><code>M-x customize-group RET markdown-ts RET</code> and go through it. Some of
the customs worth a look at first:</p>
<ul>
<li><code>markdown-ts</code> for display: markup hiding, ellipsis, bullets,
checkboxes, thematic break and hard line break characters, inline
images, folding on open</li>
<li>code blocks: <code>markdown-ts-code-block-modes</code>,
<code>markdown-ts-default-code-block-mode</code>,
<code>markdown-ts-enable-code-block-context-mode</code></li>
<li>tables: <code>markdown-ts-enable-table-mode</code>,
<code>markdown-ts-table-auto-align</code>,
<code>markdown-ts-table-default-column-width</code></li>
<li><code>markdown-ts-convert</code> for exporting</li>
<li><code>markdown-ts-toc</code> for tables of contents</li>
</ul>
<p>Faces are customizable too, one per Markdown element.</p>
<h2>How you can help</h2>
<p>The best way you can help is simply by using it. Try it with your
Markdown files, play with the different features, and see what needs
improvement or what breaks.</p>
<p>If you find something that doesn't work as expected, please report it
as a bug from Emacs itself with <code>M-x report-emacs-bug RET</code>. Include a
small example that reproduces the problem whenever possible. This is
especially useful for issues involving <em>fontification</em>, tree-sitter
grammars, tables, code blocks, or interactions with other modes.</p>
<p>We're still polishing the rough edges, so bug reports, feedback, and
real-world testing are very welcome.</p>
<h2>I found a bug, is it because <code>markdown-ts-mode</code> is buggy?</h2>
<p>Some of the surprises you may hit while using <code>markdown-ts-mode</code> might
be the mode, some might be the grammar, some might come from how
tree-sitter is integrated into Emacs, or from the tree-sitter
ecosystem as a whole. Knowing about this upfront helps understanding
that debugging is challenging.</p>
<h3>Grammars are a shared, external asset</h3>
<p>A grammar is not written for Emacs. The very same
<code>tree-sitter-markdown</code> is consumed by other editors and tools, so any
change to it is negotiated among all of its users. That is great for
the ecosystem, and it also means a fix we would like to see may take a
while to land, or may never land in the shape we would prefer. When
that happens, we work around it inside the mode as best we can, and
report the issue upstream.</p>
<p>So, if you find something that looks like a mode bug and the answer
turns out to be "the grammar parses it this way", now you know where
that answer comes from. Please do report it anyway, we would rather
hear about it twice than not at all.</p>
<p>Building grammars has its own quirks too. Not every grammar builds
with <code>make</code> and a C compiler alone: several are generated from a
JavaScript definition, so their build path expects the <code>tree-sitter</code>
CLI, and sometimes a Node.js installation, to be available. This is a
good part of why pre-compiled grammar bundles and distro packages are
so popular. As said before, I still prefer compiling them
interactively from Emacs, but now you know why your distro may be
pulling in more than you expected.</p>
<h3>Indirect buffers</h3>
<p>This one deserves an explicit warning, because it surprises people:
tree-sitter and indirect buffers do not get along.</p>
<ol>
<li>
<p>Parsers are not shared with indirect buffers. They belong to the
base buffer, and an indirect buffer starts with none. You either
copy them over manually, or re-instantiate them by enabling a major
mode in the indirect buffer.</p>
</li>
<li>
<p>Font-lock in indirect buffers is not supported at all. This is a
limitation in Emacs itself.</p>
</li>
</ol>
<p>The practical consequence is that (at least at the moment of this
writing) if you use a package that clones a region into an indirect
buffer, expect no fontification there. This is not specific to
<code>markdown-ts-mode</code>, it applies to every <code>-ts-mode</code>, and it is not
something we can fix from the mode's side.</p>
<h2>Further reading</h2>
<p>If this guide got you interested, there is a lot of good material out
there about writing and using tree-sitter modes. Stéphane Marks, my
partner in crime on this mode, put together the list below, and it is
too good to keep to ourselves. Some of it may be a little stale by
now, tree-sitter moves fast, but the reasoning in these articles holds
up:</p>
<ul>
<li><a href="https://blog.pulsar-edit.dev/tag/tree-sitter/">Pulsar Edit's tree-sitter series</a>,
another editor going through the same journey</li>
<li><a href="https://batsov.com/articles/2026/02/27/building-emacs-major-modes-with-treesitter-lessons-learned/">Building Emacs major modes with tree-sitter: lessons learned</a></li>
<li><a href="https://emacsredux.com/blog/2026/07/17/tree-sitter-modes-still-need-a-syntax-table/">Tree-sitter modes still need a syntax table</a></li>
<li><a href="https://www.masteringemacs.org/article/lets-write-a-treesitter-major-mode">Let's write a tree-sitter major mode</a></li>
<li><a href="https://magnus.therning.org/2023-03-22-making-an-emacs-major-mode-for-cabal-using-tree-sitter.html">Making an Emacs major mode for Cabal using tree-sitter</a></li>
</ul>
<p>And, of course, the notes from the people who built all of this into
Emacs, Yuan Fu and Juri Linkov, which are the closest thing we have to
a canonical reference:</p>
<ul>
<li><a href="https://archive.casouri.cc/note/2024/emacs-30-tree-sitter/">Emacs 30 tree-sitter notes</a></li>
<li><a href="https://github.com/emacs-mirror/emacs/tree/master/admin/notes/tree-sitter">admin/notes/tree-sitter in the Emacs repository</a></li>
</ul>
<h2>Is this going to be out of the <code>experimental</code> tag on next Emacs release?</h2>
<p>In this post beginning I wrote:</p>
<blockquote>
<p>What this means? Should you use it or not? Is this ready? Is this just a sketch of a mode?</p>
</blockquote>
<p>Now you probably have a better answer.</p>
<p><code>experimental</code> does not mean <code>markdown-ts-mode</code> is just a sketch or
that it is missing the basic features you would expect from a Markdown
mode.  It means the mode is still evolving, and we are not yet ready
to promise that its API, behavior, or some of its features won't
change.</p>
<p>So, should you use it? <strong>Yes!</strong> If you are comfortable with the
experimental label, please give it a try. The more people using it
with different Markdown files, configurations, and workflows, the
easier it is for us to find issues and fix it.</p>
<p>Will it be out of <code>experimental</code> in the next Emacs release? Maybe, we
sure are working towards it! We will see. There are still things to
polish, limitations to work around, and feedback to process before we
can make that call.</p>
<p>For now, consider this your invitation to play with it. And if you
find something weird, don't just work around it, let us know. That's
how we get it ready.</p>
</body></html>]]></content>
        <author>
            <name>Rahul Juliato</name>
            <uri>https://rahuljuliato.com</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[James Cherti: Configuring Emacs Scrolling for Better Usability]]></title>
        <id>https://www.jamescherti.com/emacs-scrolling-better-performance-usability/</id>
        <link href="https://www.jamescherti.com/emacs-scrolling-better-performance-usability/"/>
        <updated>2026-08-26T16:18:54.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>By default, scrolling in Emacs recenters the window when point moves off-screen, and rapid scrolling through large, heavily fontified files can introduce noticeable input lag. This article outlines configurations that make scrolling more predictable and responsive.</p>



<h2>Customizing scroll recentering</h2>



<p>Adjusting the automatic scrolling behavior can prevent Emacs from making large jumps when point moves beyond the visible portion of the window:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Scroll by up to 20 lines to bring point back into view before falling back to</span>
<span class="hljs-comment">;; the normal automatic scrolling behavior.</span>
(<span class="hljs-name">setq</span> scroll-conservatively <span class="hljs-number">20</span>)</code></span></pre>


<p>When point moves off-screen or into the scroll margin, setting <code>scroll-conservatively</code> to a moderate value like 20 allows Emacs to scroll the text by up to 20 lines in either direction to bring point back into view.</p>



<p>Note: Setting <code>scroll-conservatively</code> to a value above 100 prevents automatic scrolling from centering point, regardless of how far point moves. Emacs instead scrolls only far enough to bring point into view, placing it at the top or bottom of the window depending on the direction of scrolling.</p>



<h2>Maintaining vertical context</h2>



<p>While <code>scroll-conservatively</code> controls how Emacs reacts when point leaves the window, you can also define a boundary to force scrolling before point reaches the absolute edge:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Keep 3 lines of context visible above and below point.</span>
(<span class="hljs-name">setq</span> scroll-margin <span class="hljs-number">3</span>)</code></span></pre>


<p>Setting <code>scroll-margin</code> to 3 establishes a boundary of three lines at both the top and bottom of the Emacs window, forcing the buffer to scroll automatically as soon as your cursor enters this area instead of waiting for it to reach the absolute edge of the screen.</p>



<p>This keeps point away from the top and bottom edges of the window whenever automatic scrolling can move it out of the margin.</p>



<p>Note: Leaving <code>scroll-margin</code> at the default of <code>0</code> ensures the cursor can sit directly on the top or bottom line before triggering a scroll. Many users prefer this default setting because it maximizes usable vertical screen space and mirrors the edge-scrolling behavior found in most other modern text editors.</p>



<h2>Horizontal scrolling</h2>



<p>When line truncation is enabled (<code>truncate-lines</code> is non-nil), Emacs automatically scrolls horizontally when point approaches the left or right edge of the window. <code>hscroll-margin</code> controls how close point can get to an edge, while <code>hscroll-step</code> controls how far the window moves when automatic horizontal scrolling occurs.</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Horizontal scrolling</span>
(<span class="hljs-name">setq</span> hscroll-margin <span class="hljs-number">2</span>
      hscroll-step <span class="hljs-number">1</span>)</code></span></pre>


<p>Setting <code>hscroll-margin</code> to <code>2</code> causes horizontal scrolling to trigger when point comes within two columns of the left or right edge, while setting <code>hscroll-step</code> to <code>1</code> forces the window to pan exactly one column at a time. </p>



<p>This transforms horizontal movement into a column-by-column scroll, replacing the default behavior where Emacs jumps the view by half a screen horizontally and forces you to visually search for your cursor.</p>



<h2>Deferring fontification during input</h2>



<p>Fontification (syntax highlighting) requires CPU time to parse and colorize text. In large or complex buffers, this process can block the main thread, leading to input latency. Setting <code>redisplay-skip-fontification-on-input</code> to <code>t</code> causes Emacs to prioritize user input over immediate syntax highlighting:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Skip some fontification when input is pending.</span>
(<span class="hljs-name">setq</span> redisplay-skip-fontification-on-input <span class="hljs-literal">t</span>)</code></span></pre>


<p>The tradeoff is that syntax highlighting may temporarily lag behind the underlying text while your input is actively being processed. When scrolling rapidly into an unseen section of a file or pasting a large block of code, the text might appear uncolored or incorrectly colored for a fraction of a second. However, the delay is virtually unnoticeable, and the correct colors will render as soon as the input queue clears.</p>



<h2>Preserving screen position</h2>



<p>Setting <code>scroll-preserve-screen-position</code> to <code>t</code> fixes a common visual annoyance when paging up or down through a file (using <code>C-v</code> or <code>M-v</code>). If your cursor is in the middle of the screen and you hit Page Down, the cursor stays exactly in the middle of your monitor:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Preserve point's vertical screen position when scrolling.</span>
(<span class="hljs-name">setq</span> scroll-preserve-screen-position <span class="hljs-literal">t</span>)</code></span></pre>


<p>Note: There is one exception involving <code>next-screen-context-lines</code> (default 2), which controls how many lines of text are repeated from your previous screen when you scroll by a full window. If your cursor is resting on one of these repeated lines at the edge of the window when you jump, Emacs does not preserve its vertical screen position. Instead, it moves the cursor into the newly revealed text. This prevents the cursor from getting stranded at the extreme edge of the window and ensures you have enough surrounding text to read comfortably.</p>



<h2>Disabling automatic vertical scrolling</h2>



<p>Setting <code>auto-window-vscroll</code> to <code>nil</code> prevents movement and scrolling functions from automatically modifying the window's vertical scroll position when they encounter display rows taller than the window:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Do not automatically adjust vertical scrolling through tall display rows.</span>
(<span class="hljs-name">setq</span> auto-window-vscroll <span class="hljs-literal">nil</span>)</code></span></pre>


<p>This makes vertical movement through buffers containing large elements, such as inline images, much more predictable by avoiding sudden partial-screen shifts. The tradeoff is that tall display rows become more cumbersome to navigate because Emacs no longer automatically scrolls through their partially visible portions to reveal the rest of the element.</p>



<h2>Top and bottom scroll errors</h2>



<p>By default, Emacs immediately signals an error if you attempt to scroll past the top or bottom of a buffer. Setting <code>scroll-error-top-bottom</code> to <code>t</code> changes this behavior so that your first attempt to scroll past the boundary safely moves the cursor to the exact beginning or end of the document:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Move point to the buffer boundary before signaling a scrolling error.</span>
(<span class="hljs-name">setq</span> scroll-error-top-bottom <span class="hljs-literal">t</span>)</code></span></pre>


<p>Instead of throwing an error immediately, it moves your cursor directly to the first or last character of the buffer. If you attempt to scroll again while your cursor is already resting on that final position, Emacs will then signal the standard scrolling error.</p>



<h2>Enabling faster scrolling</h2>



<p>Rapid scrolling can become sluggish when Emacs encounters previously unfontified text. Setting <code>fast-but-imprecise-scrolling</code> to <code>t</code> prevents Emacs from becoming unresponsive when you move rapidly through large buffers:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Avoid fontifying unfontified text while scrolling rapidly.</span>
(<span class="hljs-name">setq</span> fast-but-imprecise-scrolling <span class="hljs-literal">t</span>)</code></span></pre>


<p>The primary tradeoff is that scrolling can become visually imprecise. However, this behavior is generally nothing to worry about. For typical programming tasks using a standard monospaced font, line heights remain consistent, meaning you will likely never experience this imprecision at all. Even in modes that do use variable font sizes, the visual jump is minor and temporary. Once you stop scrolling and the input queue clears, Emacs finishes fontifying the visible text and corrects the layout, ensuring your final view is accurate.</p>



<h2>Scroll aggressiveness</h2>



<p>By default, when point moves beyond the top or bottom of the window, Emacs scrolls the buffer and places point around the middle of the screen. Setting <code>scroll-up-aggressively</code> and <code>scroll-down-aggressively</code> to <code>0.01</code> keeps point near the edge of the screen instead:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Provide a "stick-to-edge" scrolling experience.</span>
(<span class="hljs-name">setq-default</span> scroll-up-aggressively <span class="hljs-number">0.01</span>
              scroll-down-aggressively <span class="hljs-number">0.01</span>)</code></span></pre>


<p>This results in minimal, predictable scrolling increments.</p>



<h2>Permitting scrolling during search</h2>



<p>By default, attempting to scroll the window while in an active <code>isearch</code> session (using <code>C-s</code> or <code>C-r</code>) cancels the search. You can allow scrolling without losing your search context.</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Allow scrolling actions while remaining inside a search block.</span>
(<span class="hljs-name">setq</span> isearch-allow-scroll 'unlimited)</code></span></pre>


<p>This allows you to scroll away from your current match to check another part of the file for reference, and then resume your search by pressing <code>C-s</code> to jump to the next match.</p>



<h2>Shell and compilation buffers</h2>



<p>When executing long-running processes in interactive shells or REPLs (<code>comint-mode</code>), new output arriving at the bottom of the buffer will frequently scroll your view downward. Setting comint-scroll-to-bottom-on-input to t and comint-scroll-to-bottom-on-output to nil causes Emacs to snap point to the bottom of the buffer only when you supply keyboard input:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Auto-scroll to bottom only when you type, not when background output arrives.</span>
(<span class="hljs-name">setq-default</span> comint-scroll-to-bottom-on-input <span class="hljs-literal">t</span>
              comint-scroll-to-bottom-on-output <span class="hljs-literal">nil</span>)</code></span></pre>


<p>This allows you to scroll up through compilation logs or terminal history to read errors without the screen aggressively jumping to the bottom every time a new line is printed.</p>



<h2>The mouse wheel scrolling</h2>



<p>The default mouse wheel behavior in Emacs can feel jumpy, as it scrolls multiple lines per click and accelerates based on scroll speed. You can easily modify this and assign modifier keys to perform horizontal scrolling or text scaling:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Scroll one line at a time and map modifier keys to specific actions.</span>
(<span class="hljs-name">setq</span> mouse-wheel-scroll-amount
      '(<span class="hljs-number">1</span>
        ((shift) . hscroll) ((meta))
        ((control meta) . global-text-scale)
        ((control) . text-scale)))

<span class="hljs-comment">;; Disable acceleration of scrolling.</span>
(<span class="hljs-name">setq</span> mouse-wheel-progressive-speed <span class="hljs-literal">nil</span>)</code></span></pre>


<p>Setting <code>mouse-wheel-scroll-amount</code> to <code>1</code> configures the mouse wheel to scroll exactly one line vertically, while mapping modifier keys like Shift for horizontal scrolling and Control for text scaling turns your mouse into a navigation multi-tool. Paired with setting <code>mouse-wheel-progressive-speed</code> to <code>nil</code>, this prevents rapid wheel movements from dynamically accelerating the scroll distance.</p>



<p>The result is a predictable, line-by-line scrolling experience that stops you from losing your place, with the tradeoff being that navigating very long files with a physical scroll wheel requires more physical effort since the view will not jump in large increments.</p>



<p>Note: If you prefer a keyboard-driven workflow and want to disable mouse input entirely, check out the <a href="https://github.com/jamescherti/inhibit-mouse.el">inhibit-mouse</a> package.</p>



<h2>Modern pixel-precise scrolling</h2>



<p>Emacs 29 introduced pixel-precise scrolling for pointing devices that support suitable high-resolution scrolling events.</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Enable pixel-precise scrolling for supported pointing devices.</span>
(<span class="hljs-name">pixel-scroll-precision-mode</span> <span class="hljs-number">1</span>)

<span class="hljs-comment">;; (setq pixel-scroll-precision-use-momentum nil) ; Optional: disable momentum</span></code></span></pre>


<p>Enabling <code>pixel-scroll-precision-mode</code> activates pixel-resolution scrolling for supported mouse and touchpad input, removing the traditional limitation of scrolling by whole text lines. </p>
<div class="yarpp yarpp-related yarpp-related-rss yarpp-template-list">

<h3>Related posts:</h3><ol>
<li><a href="https://www.jamescherti.com/emacs-fix-org-mode-copy-paste-yank-bleed/">Copy-paste without Emacs org-mode or markdown-mode Formatting Bleeding Into Other Buffers</a></li>
<li><a href="https://www.jamescherti.com/emacs-eglot-performance/">Configuring Emacs Eglot for Better Performance and Latency</a></li>
<li><a href="https://www.jamescherti.com/emacs-why-use-setq-instead-setopt/">Emacs startup - Why setq beats setopt, customize-set-variable, and use-package :custom?</a></li>
<li><a href="https://www.jamescherti.com/emacs-compile-angel-byte-native-compile/">The compile-angel Emacs package: Byte-compile and Native-compile Emacs Lisp libraries Automatically</a></li>
<li><a href="https://www.jamescherti.com/emacs-ultisnips-mode-edit-snippets-files/">ultisnips-mode.el - An Emacs major mode for editing Ultisnips snippet files (*.snippets files)</a></li>
<li><a href="https://www.jamescherti.com/easysession-el-persist-restore-emacs-session/">easysession.el: Easily persist and restore Emacs sessions (windows, tab-bar, file buffers, scratch, Dired, narrowing, indirect buffers/clones, Magit buffers...); a robust desktop.el replacement</a></li>
<li><a href="https://www.jamescherti.com/essential-emacs-packages/">Must-have Emacs Packages for Efficient Software Development and Text Editing</a></li>
</ol>
</div>
</body></html>]]></content>
        <author>
            <name>James Cherti</name>
            <uri>https://www.jamescherti.com</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Irreal: Fixing Define-word]]></title>
        <id>https://irreal.org/blog/?p=14040</id>
        <link href="https://irreal.org/blog/?p=14040"/>
        <updated>2026-08-26T15:08:06.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
My go to Emacs in-line dictionary is abo-abo’s <a href="https://github.com/abo-abo/define-word">define-word</a>. It pops up a definition of the word at point in the minibuffer. I use it several times a day. Lately, though, it has been returning “zero definitions found” on every invocation. This problem predated my update to Emacs 31.1 so it’s not related to the version of Emacs.
</p>
<p>
Today (Tuesday) I finally got fed up and decided to track down the problem. It wasn’t too hard. It turns out that the problem is that <code>define-word</code> calls the on-line dictionaries with HTTP rather than HTTPS and the sites are rejecting the connections. There’s already a <a href="https://github.com/abo-abo/define-word/pull/34/commits/7b8dda4f559d78a3f5e1421bfd48db193994727a">pull request</a> (2026-08-22) for the fix but as of today (2026-08-25) it hasn’t been merged or uploaded to MELPA.
</p>
<p>
I messed around for a while but couldn’t get any of the obvious solutions to work so I gave up and added
</p>
<div class="org-src-container">
<pre><code>  <span>:init</span>
  <span>;; </span><span>Until define-word is updated in MELPA
</span>  (<span>defcustom</span> <span>define-word-services</span>
    '((wordnik <span>"https://wordnik.com/words/%s"</span> define-word--parse-wordnik)
      (openthesaurus <span>"https://www.openthesaurus.de/synonyme/%s"</span> define-word--parse-openthesaurus)
      (webster <span>"https://webstersdictionary1828.com/Dictionary/%s"</span> define-word--parse-webster)
      (offline-wikitionary define-word--get-offline-wikitionary nil))
    <span>"Services for define-word, A list of lists of the
  format (symbol url function-for-parsing).
Instead of an url string, url can be a custom function for retrieving results."</span>
    <span>:type</span> '(alist
            <span>:key-type</span> (symbol <span>:tag</span> <span>"Name of service"</span>)
            <span>:value-type</span> (group
                         (string <span>:tag</span> <span>"Url (%s denotes search word)"</span>)
                         (<span>function</span> <span>:tag</span> <span>"Parsing function"</span>))))
</code></pre>
</div>
<p>
to the <code>use-package</code> for <code>define-word</code>. That’s just a copy of the definition of <code>define-word-services</code> from the <code>define-word</code> source. Note that it’s important that it goes in the <code>:init</code> section so that the definition gets established before <code>define-word</code> is loaded.
</p>
<p>
It’s a messy solution but it will do until the fix is merged and percolates up to MELPA.</p>
</body></html>]]></content>
        <author>
            <name>Irreal</name>
            <uri>https://irreal.org/blog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Emacs Redux: Meet Utterson, my Jekyll blogging helper]]></title>
        <id>https://emacsredux.com/blog/2026/08/26/meet-utterson-my-jekyll-blogging-helper/</id>
        <link href="https://emacsredux.com/blog/2026/08/26/meet-utterson-my-jekyll-blogging-helper/"/>
        <updated>2026-08-26T14:24:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>Back in 2019 I wrote about <a href="https://emacsredux.com/blog/2019/05/21/dealing-with-jekyll-post-urls/">dealing with Jekyll post URLs</a>,
where I shared a tiny command that spared me from having to remember the exact
file name of every article I wanted to link to. That command has been in my config
ever since, and over the years it quietly grew a few siblings. Recently I finally
gathered them all in one place and turned them into a small package -
<a href="https://github.com/bbatsov/utterson">utterson</a>.</p>

<h2>The common Jekyll tasks</h2>

<p>I guess anyone who blogs with Jekyll knows the drill. Jekyll is great and I have
no intention of switching, but a few of its conventions generate a steady trickle
of manual work:</p>

<ul>
  <li>posts live in <code class="language-plaintext highlighter-rouge">_posts</code> and their file names have to start with the publication
date, as in <code class="language-plaintext highlighter-rouge">2026-08-26-some-post.md</code></li>
  <li>every article needs a bit of YAML front matter at the top - a layout, a title,
a date, some tags</li>
  <li>linking to another post means using a Liquid tag that wants that date-prefixed
file name, which nobody remembers</li>
  <li>images and other assets need URLs relative to the site root, not to the article
you happen to be editing</li>
</ul>

<p>None of this is hard. All of it is annoying, and all of it is exactly the sort of
mechanical work an editor should be doing on your behalf. So over the years I
wrote a command for each chore, dropped it in my <code class="language-plaintext highlighter-rouge">init.el</code> and moved on with my
life.</p>

<h2>Why I finally cleaned this up</h2>

<p>The tipping point was the last command I added - promoting a draft to a post.</p>

<p>I write most articles in <code class="language-plaintext highlighter-rouge">_drafts</code>, where <code class="language-plaintext highlighter-rouge">jekyll serve --drafts</code> shows them but a
deploy doesn’t. Publishing one used to mean renaming the file by hand to add
today’s date, then editing the <code class="language-plaintext highlighter-rouge">date</code> in the front matter to match, then
convincing git that this was a rename rather than a delete plus a brand new file.
Three chances to get something wrong, and I certainly got it wrong more than once.</p>

<p>Once that was automated I took a look at what had accumulated in my config and
realized it wasn’t a bag of snippets anymore - it was a workflow. And since every
person blogging with Jekyll from Emacs deals with exactly the same chores, there
was no good reason to keep it to myself. So I spent an evening extracting the
lot, wrote some tests, and gave it a name.<sup><a href="https://emacsredux.com/#fn:1">1</a></sup></p>

<p>There are other Jekyll packages for Emacs, of course. Both
<a href="https://github.com/nibrahim/Hyde">hyde</a> and
<a href="https://github.com/masasam/emacs-easy-jekyll">easy-jekyll</a> aim to be something
like an IDE for your site, with dashboards, deployment and preview buffers. That’s
a perfectly reasonable thing to want, it’s just never been what I wanted. My site
lives in git, I deploy it with a push, and I’ve already got Magit and a browser.
All I ever wanted was for the bookkeeping to stop being my problem.</p>

<p>So <code class="language-plaintext highlighter-rouge">utterson</code> is deliberately small. It knows that a Jekyll site is a folder of
Markdown files with YAML front matter, and that’s about the extent of its
ambitions. Renames go through <code class="language-plaintext highlighter-rouge">vc</code>, so git records them as renames and the history
of an article follows it around. Nothing in there ever deploys anything.</p>

<h2>Taking it for a spin</h2>

<p>The package is not on MELPA (yet?), so for the time being it’s <code class="language-plaintext highlighter-rouge">package-vc</code>
territory:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="nb">use-package</span> <span class="nv">utterson</span>
  <span class="ss">:vc</span> <span class="p">(</span><span class="ss">:url</span> <span class="s">"https://github.com/bbatsov/utterson"</span> <span class="ss">:rev</span> <span class="ss">:newest</span><span class="p">)</span>
  <span class="ss">:custom</span>
  <span class="c1">;; where to look for sites, when you invoke a command outside one</span>
  <span class="p">(</span><span class="nv">utterson-search-path</span> <span class="o">'</span><span class="p">(</span><span class="s">"~/projects/"</span><span class="p">))</span>
  <span class="ss">:config</span>
  <span class="c1">;; the commands live in a keymap that you bind wherever you please</span>
  <span class="p">(</span><span class="nv">keymap-set</span> <span class="nv">utterson-mode-map</span> <span class="s">"C-c j"</span> <span class="ss">'utterson-command-map</span><span class="p">)</span>
  <span class="p">(</span><span class="nv">global-utterson-mode</span> <span class="mi">+1</span><span class="p">))</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">global-utterson-mode</code> enables the minor mode in the buffers of any folder that
has a <code class="language-plaintext highlighter-rouge">_config.yml</code> in it, so the commands are around while you’re working on a
site and nowhere else.</p>

<p><code class="language-plaintext highlighter-rouge">C-c j p</code> starts a post. It asks for a title, offers a file name slug derived from
it (which you’re free to shorten right there, and I usually do), then asks for
tags, completing against every tag the site already uses. That last bit is how you
avoid ending up with both <code class="language-plaintext highlighter-rouge">Emacs</code> and <code class="language-plaintext highlighter-rouge">emacs</code> in your tag cloud. Finally it writes
the front matter, saves the file and leaves the cursor exactly where you’re about
to start writing:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre><code><span class="nn">---</span>
<span class="na">layout</span><span class="pi">:</span> <span class="s">post</span>
<span class="na">title</span><span class="pi">:</span> <span class="s">Meet Utterson, my Jekyll blogging helper</span>
<span class="na">date</span><span class="pi">:</span> <span class="s">2026-08-26 15:01 +0300</span>
<span class="na">tags</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">Jekyll</span>
<span class="pi">-</span> <span class="s">Blogs</span>
<span class="pi">-</span> <span class="s">Packages</span>
<span class="nn">---</span>
</code></pre></div></div>

<p>That block, as it happens, is the one this very article started with. Here’s the
whole thing in action:</p>

<p><img src="https://emacsredux.com/assets/images/utterson-new-post.gif" alt="Creating a new post with utterson: the title prompt, the slug derived from it, tag completion, and the finished front matter"></p>

<p><code class="language-plaintext highlighter-rouge">C-c j d</code> does the same in <code class="language-plaintext highlighter-rouge">_drafts</code> and <code class="language-plaintext highlighter-rouge">C-c j t</code> moves an article between the
two, dating it on the way in and staging the rename with git. <code class="language-plaintext highlighter-rouge">C-c j r</code> is for the
article you wrote a couple of weeks ago and never got around to publishing - it
sets the date to now and renames the file to match. Both of them take a prefix
argument if you’d rather type a date than accept the current time, which is how
you schedule something for next Monday morning.</p>

<p>Publishing a draft looks like this - notice that the file name, the <code class="language-plaintext highlighter-rouge">date</code> in the
front matter and git’s idea of what happened all stay in sync:</p>

<p><img src="https://emacsredux.com/assets/images/utterson-publish-draft.gif" alt="Publishing a draft with utterson: the file is renamed with today's date, the front matter date is updated, and git records a rename"></p>

<p>The linking commands are the descendants of that 2019 snippet, all grown up.
<code class="language-plaintext highlighter-rouge">C-c j u</code> inserts a <code class="language-plaintext highlighter-rouge">post_url</code> tag, completing over your posts newest first and
showing you each post’s title next to its file name, and <code class="language-plaintext highlighter-rouge">C-c j l</code> inserts the
entire Markdown link, already titled after the post it points to:</p>

<p><img src="https://emacsredux.com/assets/images/utterson-insert-post-link.gif" alt="Inserting a link to another post: the completion list shows every post newest first, with its title next to the file name"></p>

<p><code class="language-plaintext highlighter-rouge">C-c j i</code>
inserts an image, <code class="language-plaintext highlighter-rouge">C-c j I</code> copies a file into your images folder first (perfect
for a screenshot that’s still sitting on your desktop under a name like
<code class="language-plaintext highlighter-rouge">Screenshot 2026-08-26 at 15.01.12.png</code>), and <code class="language-plaintext highlighter-rouge">C-c j a</code> links to anything else
under <code class="language-plaintext highlighter-rouge">assets</code>. And <code class="language-plaintext highlighter-rouge">C-c j s</code> runs <code class="language-plaintext highlighter-rouge">jekyll serve</code> in a buffer, because sooner or
later you do want to look at the thing.</p>

<p><strong>Note:</strong> There’s a menu as well, in case you’d rather click your way around while
the keybindings are still settling in.</p>

<h2>Epilogue</h2>

<p>It’s early days for the package, even though most of the code behind it has been
in daily use for years. If you blog with Jekyll from Emacs, I’d love to hear which
of your own little chores it doesn’t cover yet - that’s the fastest way for it to
get better.</p>

<p>And if you don’t blog with Jekyll - what does your setup look like? I’ve got the
feeling that quite a few of you are writing your posts in <code class="language-plaintext highlighter-rouge">org-mode</code> and I’m
always curious about the workflows people come up with.</p>

<p>That’s all I have for you today! Keep hacking!</p>

<div class="footnotes">
  <ol>
    <li>
      <p>Gabriel John Utterson is Doctor Jekyll’s loyal friend and lawyer - the man
who keeps his affairs in order. Naming things is hard, but every now and then
you get lucky.&nbsp;<a href="https://emacsredux.com/#fnref:1">↩</a></p>
    </li>
  </ol>
</div></body></html>]]></content>
        <author>
            <name>Emacs Redux</name>
            <uri>https://emacsredux.com/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Meta Redux: Smarter Form Targeting Is Coming to CIDER]]></title>
        <id>https://metaredux.com/posts/2026/08/26/smarter-form-targeting-is-coming-to-cider.html</id>
        <link href="https://metaredux.com/posts/2026/08/26/smarter-form-targeting-is-coming-to-cider.html"/>
        <updated>2026-08-26T09:18:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>If I had a dollar for every time someone asked on the Clojurians Slack in
<code class="language-plaintext highlighter-rouge">#cider</code> why <code class="language-plaintext highlighter-rouge">C-x C-e</code> evaluated “the wrong thing”, I’d probably be writing this
post from a yacht.  The answer was always the same: the cursor wasn’t where
CIDER expected it to be. The <em>upcoming</em> CIDER 2.1 release changes that - the
evaluation commands now figure out which form you mean from where your cursor
actually is.</p>



<p>NOTE: <strong>Update:</strong> this didn’t survive contact with its users, and CIDER 2.1
ships with the classic behaviour after all. See
/posts/2026/08/29/smarter-form-targeting-is-not-coming-to-cider.html for
what the feedback was, what replaced it, and the rather nasty bug the detour
turned up.</p>

<h2>A bit of history</h2>

<p>Emacs has a very particular tradition when it comes to evaluating code:
<code class="language-plaintext highlighter-rouge">eval-last-sexp</code> (the venerable <code class="language-plaintext highlighter-rouge">C-x C-e</code>) acts on the expression <em>before</em> the
cursor. Not the one you’re looking at, not the one you’re inside of - the one
that ends exactly where your cursor stands. SLIME follows this tradition,
Emacs Lisp itself follows it, and for the past 15+ years CIDER has followed it
too.<sup><a href="https://metaredux.com/#fn:1">1</a></sup> If you grew up in Emacs, this rule is in your fingers and you’ve
never once thought about it.</p>

<p>Here’s the thing, though - most people using CIDER didn’t grow up in Emacs.
And many of them never programmed in Emacs Lisp and Common Lisp with SLIME.
They came to Emacs <em>because of</em> CIDER (or Clojure in general), and for them
the rule is invisible, arbitrary and mildly hostile. You put your cursor on a
form, you press the eval key, and CIDER cheerfully evaluates… something
else. Meanwhile every other modern Clojure environment - Calva, Conjure, the
various vim plugins - resolves the form from the cursor position and just does
what you meant.<sup><a href="https://metaredux.com/#fn:2">2</a></sup></p>

<p>For a long time I resisted changing this, mostly out of respect for the Emacs
tradition (and my own muscle memory). But at some point I had to admit that I
was optimizing for the wrong audience. CIDER’s users are mostly casual
Clojure hackers who happen to use Emacs, not Emacs experts who happen to
write Clojure. The tradition was serving me, and confusing them.</p>

<h2>What’s actually changing</h2>

<p>The evaluation commands (and their macroexpansion, inspection and tapping
siblings) now resolve “the form the cursor indicates”. Concretely, with <code class="language-plaintext highlighter-rouge">|</code>
marking the cursor:</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="nb">map</span><span class="w"> </span><span class="nb">inc</span><span class="w"> </span><span class="err">|</span><span class="p">(</span><span class="nb">range</span><span class="w"> </span><span class="mi">10</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p>Pressing <code class="language-plaintext highlighter-rouge">C-c C-e</code> here used to evaluate <code class="language-plaintext highlighter-rouge">inc</code> - the form <em>before</em> the cursor,
which is almost never what you wanted. Now it evaluates <code class="language-plaintext highlighter-rouge">(range 10)</code> - the
form your cursor is pointing at.</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="nb">str</span><span class="w"> </span><span class="s">"hello"</span><span class="w"> </span><span class="s">" "</span><span class="w"> </span><span class="s">"world"</span><span class="err">|</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<p>This one used to evaluate <code class="language-plaintext highlighter-rouge">"world"</code> (really!), because the last complete
expression before a cursor sitting on the closing paren is the final string.
Now it evaluates the whole <code class="language-plaintext highlighter-rouge">(str ...)</code> call.</p>

<p>Macroexpansion benefits too:</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="nb">when</span><span class="w"> </span><span class="n">tru</span><span class="err">|</span><span class="n">e</span><span class="w"> </span><span class="p">(</span><span class="nf">launch-missiles</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">C-c C-m</code> here used to complain that <code class="language-plaintext highlighter-rouge">true</code> is not a macro. Now it expands the
enclosing <code class="language-plaintext highlighter-rouge">(when ...)</code> call, because expanding a bare symbol is never what
anyone means.</p>

<p>And my favorite one - the rich comment workflow is now consistent everywhere:</p>

<div class="language-clojure highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="nb">comment</span><span class="w">
  </span><span class="p">(</span><span class="nf">calculate-all-the-things</span><span class="err">|</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p>Every defun-level command - eval, pretty-print, inspect, debug - now treats
the form inside the <code class="language-plaintext highlighter-rouge">(comment ...)</code> as the top-level one. Evaluating a whole
<code class="language-plaintext highlighter-rouge">comment</code> form returns <code class="language-plaintext highlighter-rouge">nil</code> by definition, which has exactly zero uses, so
CIDER no longer does that no matter which command you reach for.</p>

<h2>Why you probably won’t notice</h2>

<p>Here’s the part I’m most pleased with: the new behavior agrees with the old
one at every position where “the form before the cursor” made sense. Cursor
right after a form? Same result as always. Cursor in the whitespace after a
form? Same. Cursor in the middle of a symbol? Same. The two behaviors only
diverge where the classic answer was something nobody ever wanted - a
previous sibling, a lone trailing atom.</p>

<p>So if your muscle memory follows the Emacs tradition, nothing changes for
you. If it doesn’t - CIDER stops punishing you for it. That’s the whole
change.</p>

<h2>Reverting to the classic behavior (for now)</h2>

<p>If you <em>do</em> want the traditional rules - maybe you genuinely use
“evaluate the previous sibling while standing on an opening paren” - one
setting restores them exactly:</p>

<div class="language-emacs-lisp highlighter-rouge"><div class="highlight"><pre><code><span class="p">(</span><span class="k">setq</span> <span class="nv">cider-form-targeting</span> <span class="ss">'preceding</span><span class="p">)</span>
</code></pre></div></div>

<p>There’s also a per-session toggle in the eval menu (<code class="language-plaintext highlighter-rouge">C-c C-v T</code>) that shows
the active mode in the mode line while you experiment.</p>

<p>This option is probably living on borrowed
time, though. I added it back when I planned to keep the classic behavior as the
default and offer smart targeting as an opt-in. Now that the roles are
reversed, an option whose only job is restoring rules almost nobody
deliberately relied on doesn’t really make much sense, and lately I’ve been
trying to trim that kind of clutter from CIDER, not add to it. Don’t be
surprised if the option quietly disappears - possibly even before the release
ships. Which is one more reason to speak up now if the classic behavior
genuinely matters to you.</p>

<h2>Farewell, “last sexp”</h2>

<p>This change forced my hand on something I’d been putting off for years - the
command names. <code class="language-plaintext highlighter-rouge">cider-eval-last-sexp</code> is a fine name for a command that
evaluates the last sexp. It’s a lie for a command that evaluates the form
your cursor indicates. So the commands got honest names:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">cider-eval-last-sexp</code> is now <code class="language-plaintext highlighter-rouge">cider-eval-form</code></li>
  <li><code class="language-plaintext highlighter-rouge">cider-eval-defun-at-point</code> is now <code class="language-plaintext highlighter-rouge">cider-eval-defun</code> (the <code class="language-plaintext highlighter-rouge">-at-point</code>
never carried information)</li>
  <li>likewise for the pprint/tap/inspect/insert variants</li>
</ul>

<p>Every old name keeps working as an alias, so your config and your <code class="language-plaintext highlighter-rouge">M-x</code>
habits are safe. But why “form” and not “sexp”? Beyond the targeting change,
there’s a Clojure-specific reason: in Clojure a <em>form</em> isn’t always a single
sexp. <code class="language-plaintext highlighter-rouge">^:private x</code> is two sexps but one form; so is <code class="language-plaintext highlighter-rouge">#inst "2024-01-01"</code>.
The commands operate on forms - the reader’s unit of evaluation - and now
they say so.<sup><a href="https://metaredux.com/#fn:3">3</a></sup> The manual’s evaluation docs got a proper glossary
explaining all of this.</p>

<h2>Closing thoughts</h2>

<p>All of this is on <code class="language-plaintext highlighter-rouge">master</code> and in the MELPA snapshots today, ahead of the
next stable release. I’d really love for people to play with it <em>before</em> the
release ships - especially if you’re an Emacs veteran whose fingers disagree
with my reasoning, or a newcomer for whom this was supposed to just work. Did
we get the resolution rules right? Does anything still surprise you?</p>

<p>Share your feedback on the <a href="https://github.com/clojure-emacs/cider/discussions">CIDER discussions</a> board, in
<code class="language-plaintext highlighter-rouge">#cider</code> on the Clojurians Slack, or just file an issue. This is exactly the
kind of change that’s easy to adjust before a release and painful after -
and the fate of the compatibility option depends on what I hear.</p>

<p>That’s all I have for you today. Keep hacking!</p>

<div class="footnotes">
  <ol>
    <li>
      <p>CIDER started its life as a SLIME “clone” for Clojure, after all - the tradition runs deep.&nbsp;<a href="https://metaredux.com/#fnref:1">↩</a></p>
    </li>
    <li>
      <p>Interestingly, Cursive is the only major non-Emacs Clojure environment that kept the classic “form before the caret” model.&nbsp;<a href="https://metaredux.com/#fnref:2">↩</a></p>
    </li>
    <li>
      <p>This also explains a subtlety Emacs veterans might appreciate: plain <code class="language-plaintext highlighter-rouge">forward-sexp</code> movement doesn’t know that Clojure metadata belongs to the form it annotates, which is why clojure-mode has always needed its own “logical sexp” movement functions. The new targeting is built on those, so metadata is never silently dropped from what you evaluate.&nbsp;<a href="https://metaredux.com/#fnref:3">↩</a></p>
    </li>
  </ol>
</div></body></html>]]></content>
        <author>
            <name>Meta Redux</name>
            <uri>https://metaredux.com/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[James Cherti: Configuring Emacs Eglot for Better Performance and Latency]]></title>
        <id>https://www.jamescherti.com/emacs-eglot-performance/</id>
        <link href="https://www.jamescherti.com/emacs-eglot-performance/"/>
        <updated>2026-08-25T16:56:08.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>Eglot ships with Emacs as a built-in, lightweight LSP client, and its default configuration is sufficient for most projects. However, working with large codebases can cause latency. When Eglot becomes sluggish, the problem may involve work performed by Eglot and Emacs as well as the language server itself. Event logging, filesystem watching, diagnostics, completion, and other editor activity can all contribute to latency.</p>



<p>This article covers practical changes for keeping Eglot responsive when working with large projects.</p>



<h2>Auto-shutting down idle Eglot servers</h2>



<p>This reduces resource usage when switching between multiple projects. The <code>eglot-autoshutdown</code> variable shuts down the LSP server after the last managed buffer has been killed.</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> eglot-autoshutdown <span class="hljs-literal">t</span>)</code></span></pre>


<p>This is useful when many separate language servers would otherwise remain running after their buffers have been closed.</p>



<p><em>(If you find yourself routinely leaving unused buffers open, the <a href="https://github.com/jamescherti/buffer-terminator.el">buffer-terminator</a> package can automate this cleanup. It quietly monitors your buffer activity and terminates idle file buffers, ensuring <code>eglot-autoshutdown</code> can trigger reliably without requiring you to manually execute <code>kill-buffer</code>.)</em></p>



<h2>Preventing Eglot from blocking the Emacs UI on connection</h2>



<p>Opening a project can cause Emacs to block while waiting for the LSP server to initialize. Setting <code>eglot-sync-connect</code> to <code>nil</code> prevents Eglot from blocking on the initial connection, allowing the connection to continue in the background:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> eglot-sync-connect <span class="hljs-literal">nil</span>)</code></span></pre>


<p>This prevents the connection attempt from unnecessarily delaying interactive editing.</p>



<h2>Disabling LSP event logging</h2>



<p>Eglot maintains an events buffer containing JSON-RPC activity. Large event logs consume memory and add string allocation costs, especially when retaining complete JSON payloads is not needed during normal editing.</p>



<p>Configuring <code>eglot-events-buffer-config</code> (or <code>eglot-events-buffer-size</code> on Emacs 29 and earlier) reduces the volume of retained data:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Disable event logging completely (Emacs &gt;= 30)</span>
(<span class="hljs-name">setq</span> eglot-events-buffer-config '(<span class="hljs-symbol">:size</span> <span class="hljs-number">0</span> <span class="hljs-symbol">:format</span> short))

<span class="hljs-comment">;; For Emacs &lt;= 29</span>
<span class="hljs-comment">;; (setq eglot-events-buffer-size 0)</span></code></span></pre>


<p>Setting <code>:size 0</code> disables the events buffer.</p>



<h2>Reducing file watchers</h2>



<p>Allowing a language server to monitor large portions of a repository can consume OS file descriptors, increase memory usage, and add startup delay. To reduce these resource costs, limit the number of file that Eglot watches using:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">setq</span> eglot-max-file-watches <span class="hljs-number">5000</span>)</code></span></pre>


<p>In addition, where supported by the language server, configure it to ignore large generated directories such as <code>node_modules</code>, <code>.git</code>, or build output folders. Because these exclusion rules are specific to the language server, they must be passed through the <code>eglot-workspace-configuration</code> variable. (The standard method is to define these rules per-project using a <code>.dir-locals.el</code> file at the root of your repository.)</p>



<h2>Disabling progress reporting</h2>



<p>Whenever a server reports progress (e.g., during indexing or builds), this can trigger mode-line redisplays. The following suppress mode-line progress reports:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Suppress mode-line progress animations</span>
(<span class="hljs-name">setq</span> eglot-report-progress <span class="hljs-literal">nil</span>)</code></span></pre>


<h2>Disabling automatic code action probing</h2>



<p>By default, Eglot can asynchronously query the language server for available code actions at point when the cursor is idle. Disabling these automatic indications reduces continuous process communication and background activity during editing:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Disable automatic code action indicators to reduce background polling</span>
(<span class="hljs-name">setq</span> eglot-code-action-indications <span class="hljs-literal">nil</span>)</code></span></pre>


<p>Note: Disabling this feature only removes the automatic UI indicators. You retain the ability to manually request and execute code actions at any time by invoking <code>M-x eglot-code-actions</code>.</p>



<h2>Disabling unneeded LSP server capabilities</h2>



<p>Note: You do not need to disable all of these capabilities, as some of them are very useful. Only disable the ones you do not use.</p>



<p>LSP servers can provide optional capabilities. Disabling features that are not needed can reduce processing and visual clutter, although the actual performance benefit depends on the language server and project.</p>



<p>Eglot handles on-type formatting by evaluating every single keystroke to check if the connected language server supports the feature and if the typed character is a registered trigger character. If a match occurs, Eglot issues an RPC request and applies the asynchronous edits, which can conflict with external formatters and introduce typing latency. To prevent these background requests and bypass the per-keystroke evaluation entirely, use:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">with-eval-after-load</span> 'eglot
  (<span class="hljs-name">add-to-list</span> 'eglot-ignored-server-capabilities <span class="hljs-symbol">:documentOnTypeFormattingProvider</span>))</code></span></pre>


<p>Inlay hints (<code>eglot-inlay-hints-mode</code>) display automatically determined types and parameter names as annotations in the buffer. Disabling them removes the associated visual annotations and any work required to maintain them:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">with-eval-after-load</span> 'eglot
  (<span class="hljs-name">add-to-list</span> 'eglot-ignored-server-capabilities <span class="hljs-symbol">:inlayHintProvider</span>))</code></span></pre>


<p>When the cursor rests on a symbol, the LSP server can highlight other occurrences of that symbol (<code>eglot-highlight-eldoc-function</code>). Disabling it will stop Eglot from highlighting other occurrences of the symbol under the cursor:</p>


<pre><span><code class="hljs language-lisp">(<span class="hljs-name">with-eval-after-load</span> 'eglot
  (<span class="hljs-name">add-to-list</span> 'eglot-ignored-server-capabilities <span class="hljs-symbol">:documentHighlightProvider</span>))</code></span></pre>


<p>For major modes with Tree-sitter support, Tree-sitter already provides syntax-aware fontification locally and can be sufficient for many users. When the language server supports semantic tokens, Eglot can request semantic-token information over LSP and use it for additional syntax highlighting. Processing these responses requires JSON-RPC communication, decoding the token data, and applying the corresponding fontification. On large or frequently changing buffers, this can add CPU overhead. Ignoring the server's semantic-token capability prevents Eglot from using this feature:</p>


<pre><span><code class="hljs language-php">(with-<span class="hljs-keyword">eval</span>-after-load <span class="hljs-string">'eglot
  (add-to-list '</span>eglot-ignored-server-capabilities :semanticTokensProvider))</code></span></pre>


<p>Note: The tradeoff is that some semantic highlighting supplied by the language server may be lost. Tree-sitter fontification and LSP semantic tokens are not identical, so disabling semantic tokens can result in less precise or less detailed highlighting for some languages.</p>



<h2>Garbage collection, native compilation, and read process output max</h2>



<ul>
<li><strong>The Emacs garbage collector</strong> can cause pauses during heavy workloads. LSP activity can increase allocation through diagnostics, completion, JSON processing, and other editor integrations. GC tuning can therefore help in some workloads, but the effect is workload-dependent. Increasing <code>gc-cons-threshold</code> permanently can reduce garbage collection frequency:<br><code>(setq gc-cons-threshold (* 100 1024 1024))</code><br><em>(Alternatively, several users rely on the <code>gcmh</code> package, which raises the GC threshold during active editing and forces a collection when Emacs becomes idle.)</em></li>



<li><strong>Enable Native Compilation</strong>: Ensure that Emacs is built with native compilation support enabled. Native compilation can improve the execution speed of Emacs Lisp code, including code used by Eglot and other packages. <em>(Recommendation: Use the <a href="https://github.com/jamescherti/compile-angel.el/">compile-angel</a> package to ensure that all packages are natively compiled.)</em></li>



<li><strong>read-process-output-max</strong>: <code>read-process-output-max</code> controls the maximum amount of data Emacs reads from a subprocess in a single operation. Since Eglot communicates with language servers through subprocesses, increasing this value can improve performance when a server sends large bursts of JSON-RPC data, such as during initialization, workspace indexing, or large file updates, by reducing the number of read operations required to consume the output. Raising it can help workloads that regularly receive large bursts of process output:<br><code>(setq read-process-output-max (* 1024 1024))</code><br><em>(This raises the limit to 1 MiB. It does not reserve 1 MiB of memory for each process or increase the amount of data a server can send; it only allows Emacs to consume more output per read operation. On GNU/Linux systems, the value should not exceed</em> <code>/proc/sys/fs/pipe-max-size</code><em>)</em></li>
</ul>



<h2>Freeing up the main Emacs thread with tree-sitter</h2>



<p>Emacs executes Lisp, handles asynchronous process output, and updates the UI on a single main thread. In traditional major modes, typing triggers complex regular expression evaluations for syntax highlighting (font-locking). This CPU-bound work competes directly with Eglot, which relies on the exact same thread to deserialize incoming JSON-RPC payloads and render diagnostics. If the main thread is busy computing regexes, Eglot is forced to wait in the queue, resulting in input lag even if the external language server responds instantly.</p>



<p>Major modes built on Tree-sitter (the <code>*-ts-mode</code> variants, such as <code>c-ts-mode</code>, <code>python-ts-mode</code>, and <code>rust-ts-mode</code>) use the Tree-Sitter C library for incremental syntax parsing. If a stable <code>*-ts-mode</code> exists for your programming language, enabling it can improve Eglot's responsiveness.</p>
<div class="yarpp yarpp-related yarpp-related-rss yarpp-template-list">

<h3>Related posts:</h3><ol>
<li><a href="https://www.jamescherti.com/emacs-fix-org-mode-copy-paste-yank-bleed/">Copy-paste without Emacs org-mode or markdown-mode Formatting Bleeding Into Other Buffers</a></li>
<li><a href="https://www.jamescherti.com/emacs-scrolling-better-performance-usability/">Configuring Emacs Scrolling for Better Usability</a></li>
<li><a href="https://www.jamescherti.com/emacs-why-use-setq-instead-setopt/">Emacs startup - Why setq beats setopt, customize-set-variable, and use-package :custom?</a></li>
<li><a href="https://www.jamescherti.com/emacs-persist-restore-text-scale/">persist-text-scale.el - Persist and Restore the Text Scale</a></li>
<li><a href="https://www.jamescherti.com/emacs-python-dev-using-eglot-pylsp-ruff-pylint-flake8/">Eglot for Python Development in Emacs: Integrating python-lsp-server (pylsp) with Linters and Formatters</a></li>
<li><a href="https://www.jamescherti.com/emacs-toggle-a-shell-window-shell-pop/">Easily Toggle an Emacs Terminal with a Single Keystroke using shell-pop (Recently Refactored)</a></li>
<li><a href="https://www.jamescherti.com/emacs-compile-angel-byte-native-compile/">The compile-angel Emacs package: Byte-compile and Native-compile Emacs Lisp libraries Automatically</a></li>
</ol>
</div>
</body></html>]]></content>
        <author>
            <name>James Cherti</name>
            <uri>https://www.jamescherti.com</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[TAONAW - Emacs and Org Mode: ]]></title>
        <id>https://taonaw.com/2026/08/25/a-quick-update-for-my.html</id>
        <link href="https://taonaw.com/2026/08/25/a-quick-update-for-my.html"/>
        <updated>2026-08-25T13:56:10.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>A quick update for my Emacs config Gems series:</p>
<p>I am working on part 5, and I’m trying to finalize a few things first. Currently it’s Ispell (I’ve not been using it to its full capacity) and completion… more details soon.</p>
</body></html>]]></content>
        <author>
            <name>TAONAW - Emacs and Org Mode</name>
            <uri>https://taonaw.com/categories/emacs-org-mode/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Charles Choi: Announcing shazam.el]]></title>
        <id>http://yummymelon.com/devnull/announcing-shazam-el.html</id>
        <link href="http://yummymelon.com/devnull/announcing-shazam-el.html"/>
        <updated>2026-08-24T21:00:00.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p><img alt="img" src="http://yummymelon.com/devnull/images/announcing-shazam/shazam-screenshot.png"></p>
<p>Continuing my explorations on malleable computing with Emacs is examining access to OS/Platform-specific resources, in this case with macOS. macOS provides a number of mechanisms for high-level <a href="https://en.wikipedia.org/wiki/Inter-process_communication">inter-process communication</a>, among them AppleScript &amp; Automator Actions (now considered legacy but still maintained) and App Intents, Entities &amp; Shortcuts (introduced in macOS 13 and considered by Apple a replacement for the former).</p>
<p>This post both announces and covers the implementation of an Emacs integration with the Shazam music recognition service that is packaged with macOS. This Emacs integration is called <code>shazam.el</code> (<a href="https://github.com/kickingvegas/shazam">https://github.com/kickingvegas/shazam</a>) and is now available for installation via <a href="https://melpa.org/#/shazam">MELPA</a>.</p>
<p>To understand the <code>shazam.el</code> implementation, the following background information is provided.</p>
<h1>Background</h1>
<p>Shazam is a music recognition service that is implemented across Apple's different platforms (macOS, iOS/iPadOS, watchOS, tvOS). This service provides a shortcut action (<em>Recognize Music</em>) which is implemented using the following APIs:</p>
<ul>
<li>
<p><strong>App Intent:</strong> An expression of an app’s capabilities to the system (in this case the ecosystem of Cocoa-based applications), and contains code to perform that action (<a href="https://developer.apple.com/documentation/appintents/app-intents">App intents | Apple Developer Documentation</a>).</p>
</li>
<li>
<p><strong>App Entity:</strong> Information provided to the system about your app’s data, or about concepts related to your app’s data (<a href="https://developer.apple.com/documentation/appintents/app-entities">App entities | Apple Developer Documentation</a>).</p>
</li>
<li>
<p><strong>App Shortcut:</strong> A workflow (including UI) for orchestrating <em>App Intents</em> and <em>Entities</em> (<a href="https://developer.apple.com/documentation/appintents/app-shortcuts">App Shortcuts | Apple Developer Documentation</a>). There are a number of ways to access a Shortcut, among them via the Shortcuts app or Siri.</p>
</li>
</ul>
<p>With the <em>Recognize Music</em> shortcut action we can realize the Elisp package <code>shazam.el</code>.</p>
<h1>Implementation</h1>
<p>A Shortcut (aka workflow) defined in the Shortcuts app can be invoked via command line as follows:</p>
<div class="highlight"><table><tbody><tr><td><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td><div><pre><span></span><code>shortcuts<span class="w"> </span>run<span class="w"> </span><span class="s2">"&lt;shortcut name&gt;"</span>
</code></pre></div></td></tr></tbody></table></div>

<p>where “shortcut name” is the name given to a Shortcut enclosed by double-quotes.</p>
<p>If the Shortcut is intended to write to <code>stdout</code>, then the <code>shortcuts</code> command line utility must be run into a pipe.</p>
<div class="highlight"><table><tbody><tr><td><div class="linenodiv"><pre><span class="normal">1</span></pre></div></td><td><div><pre><span></span><code>shortcuts<span class="w"> </span>run<span class="w"> </span><span class="s2">"&lt;shortcut name&gt;"</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>cat
</code></pre></div></td></tr></tbody></table></div>

<p>The above incantation lets Emacs run a Shortcut.</p>
<p>The Shortcut used by <code>shazam.el</code> invokes the <em>Recognize Music</em> action and does some work to transform the Shazam search result into a JSON dictionary. The JSON dictionary is written to <code>stdout</code> (<a href="https://www.icloud.com/shortcuts/bba3dd21146c4ba78dff1d7d0c0b1092">iCloud Shortcut: “Identify Music JSON”</a>) which can be read by Emacs and processed.</p>
<p>The sequence diagram below shows the overall integration workflow between Emacs and Shortcuts for <code>shazam.el</code>.</p>
<p><img alt="img" src="http://yummymelon.com/devnull/images/announcing-shazam/shazam-arch.svg"></p>
<h1>Features</h1>
<p>Details on all the features provided by <code>shazam.el</code> can be found in its <a href="https://kickingvegas.github.io/shazam/">User Guide</a>. Of note is that once the Shazam search result is deserialized, its content can be processed into a variety of formats. <code>shazam.el</code> takes advantage of this by storing recognized search results into an Org file, using Org markup and properties to store metadata provided by each result. Users can subsequently peruse this history file at their convenience.</p>
<h1>Closing Thoughts</h1>
<p>This work builds off the explorations in malleable computing with Emacs described in my prior posts:</p>
<ul>
<li><a href="http://yummymelon.com/devnull/in-emacs-everything-looks-like-a-service.html">nfdn: In Emacs, Everything Looks Like a Service</a></li>
<li><a href="http://yummymelon.com/devnull/malleable-computing-emacs-and-you.html">nfdn: Malleable Computing, Emacs, and You</a></li>
<li><a href="http://yummymelon.com/devnull/announcing-now-playing-el.html">nfdn: Announcing now-playing.el, an Emacs interface for the macOS Music app</a></li>
</ul>
<p>There is not a lot of Elisp to <a href="https://github.com/kickingvegas/shazam/blob/0e5a97c59b5972a728138fc06b2ff371d42c8b55/lisp/shazam.el">shazam.el</a> as shown in the <code>cloc</code> result for it:</p>
<div class="highlight"><table><tbody><tr><td><div class="linenodiv"><pre><span class="normal">1</span>
<span class="normal">2</span>
<span class="normal">3</span>
<span class="normal">4</span>
<span class="normal">5</span>
<span class="normal">6</span></pre></div></td><td><div><pre><span></span><code><span class="c">github</span><span class="nt">.</span><span class="c">com/AlDanial/cloc v 2</span><span class="nt">.</span><span class="c">10  T=0</span><span class="nt">.</span><span class="c">01 s (143</span><span class="nt">.</span><span class="c">3 files/s</span><span class="nt">,</span><span class="c"> 35402</span><span class="nt">.</span><span class="c">8 lines/s)</span>
<span class="nb">-------------------------------------------------------------------------------</span>
<span class="c">Language                     files          blank        comment           code</span>
<span class="nb">-------------------------------------------------------------------------------</span>
<span class="c">Lisp                             1             50             38            159</span>
<span class="nb">-------------------------------------------------------------------------------</span>
</code></pre></div></td></tr></tbody></table></div>

<p>Most of the challenge in building <code>shazam.el</code> was understanding how <code>start-process</code> worked to asynchronously run the <code>shortcuts</code> command line utility.</p>
<p>A downside to using a Shortcut (workflow) is the requirement to use a binary code-signed representation that is accessed via the Shortcuts app. This makes it less flexible than AppleScript where declaring behavior can be done in plain text. For example, the <code>now-playing.el</code> package can remote-control the Music app via AppleScript that is directly invoked from Emacs built with support for <code>ns-do-applescript</code>. In contrast, accessing contemporary macOS app behavior that is exposed via Intents and Entities requires installing (or creating) a binary code-signed representation (the Shortcut). This adds an extra step (or steps for installing multiple shortcuts) to build an integration with Emacs or any other orchestration tool.</p>
<h1>References</h1>
<ul>
<li><a href="https://github.com/kickingvegas/shazam">shazam.el Repository</a></li>
<li><a href="https://developer.apple.com/documentation/automator">Automator | Apple Developer Documentation</a></li>
<li><a href="https://developer.apple.com/documentation/AppIntents">App Intents | Apple Developer Documentation</a></li>
<li><a href="https://developer.apple.com/documentation/appintents/app-entities">App Entities | Apple Developer Documentation</a></li>
<li><a href="https://developer.apple.com/documentation/appintents/app-shortcuts">App Shortcuts | Apple Developer Documentation</a></li>
</ul></body></html>]]></content>
        <author>
            <name>Charles Choi</name>
            <uri>http://yummymelon.com/devnull/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Marcin Borkowski: Transforming yanked text]]></title>
        <id>https://mbork.pl/2026-08-24_Transforming_yanked_text</id>
        <link href="https://mbork.pl/2026-08-24_Transforming_yanked_text"/>
        <updated>2026-08-24T19:01:51.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body>Recently I found myself killing and yanking more often than ever. One problem with that is that the capitalization and punctuation of what I have yanked is often wrong. As a (pretty stupid) example, I might see the sentence “This is bad.”, and I might want to write another one: “He thinks that this is bad, and he’s right.” If I use kill-sentence (M-k) or backward-kill-sentence (C-x DEL), I have the whole This is bad. in the kill ring, but what I need to yank is this is bad (with lower-case “t” and without the period), so I need to make both changes after yanking. After several dozen such cases I thought, this is Emacs, I shouldn’t be doing this manually!</body></html>]]></content>
        <author>
            <name>Marcin Borkowski</name>
            <uri>https://mbork.pl/Homepage</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Irreal: Emacs 31.1]]></title>
        <id>https://irreal.org/blog/?p=14036</id>
        <link href="https://irreal.org/blog/?p=14036"/>
        <updated>2026-08-24T16:41:31.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
Happy news this morning. Sean Whitton has <a href="https://lists.gnu.org/archive/html/emacs-devel/2026-08/msg00760.html">announced the release of Emacs 31.1</a>. As usual, it’s available at the mirror nearest you. Note, however, that the URLs in Whitton’s announcement have a typo. See <a href="https://lists.gnu.org/archive/html/emacs-devel/2026-08/msg00764.html">this message from Eli</a> for the correct URLs.
</p>
<p>
I’m writing this with the just built new Emacs and everything seems fine. I had a bit of trouble downloading the source because, apparently, some of the mirrors haven’t updated yet. If that happens to you, just repeat the request a time or two and you should get everything. After that, enjoy your new Emacs and all the goodies it brings. As always, <a href="https://www.masteringemacs.org/article/whats-new-in-emacs-311">Mickey has his usual exegesis of the changes</a>.
</p>
<p>
As I invariably say at times like this, Eli and the rest of the development team deserve our huge thanks for their selfless efforts on our behalf.</p>
</body></html>]]></content>
        <author>
            <name>Irreal</name>
            <uri>https://irreal.org/blog</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Andros Fenollosa: A year of Org Social, my social network]]></title>
        <id>http://en.andros.dev/blog/5fcf45a2/a-year-of-org-social-my-social-network/</id>
        <link href="http://en.andros.dev/blog/5fcf45a2/a-year-of-org-social-my-social-network/"/>
        <updated>2026-08-24T15:05:32.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>A year ago I created Org Social. Just a plain-text specification and an <code>.org</code> file served over HTTP. Today it is time to take stock, to see how the project has moved, what has worked and what has not. And above all, to tell the story of a year of development and community.</p>
<p>A bird's-eye view of the whole year would look like this:</p>
<p><strong>2025</strong></p>
<ul>
<li><strong>August</strong>: the specification is born (Aug 12). Front page of Hacker News and 150 stars in 24 hours. First client, <code>org-social.el</code> (Aug 16). Spec 1.1: the first external contribution removes <code>:REPLY_URL:</code>.</li>
<li><strong>September</strong>: the Relay is born. Groups arrive in the specification.</li>
<li><strong>October</strong>: <code>org-social.el</code> v2.0 with a redesigned interface (Oct 1). It lands on MELPA (Oct 26). Proposal to the Org Mode list.</li>
<li><strong>November</strong>: Relay v1.0 and host is born. A record of 581 posts in a month, probably because the Relay indexed every account. Spec 1.4: boosting posts (<code>:INCLUDE:</code>) and domain migration (<code>:MIGRATION:</code>).</li>
<li><strong>December</strong>: Org Social overtakes twtxt in activity. Spec 1.5: posts visible only to mentioned people (<code>:VISIBILITY:</code>).</li>
</ul>
<p><strong>2026</strong></p>
<ul>
<li><strong>January</strong>: the ID moves into the headline (spec 1.6). A big change that required syncing every piece of software in the ecosystem.</li>
<li><strong>May</strong>: Org Social reaches the App Store (May 11). Spec 1.7 with the <code>:BOT:</code> property.</li>
<li><strong>July</strong>: Planet Emacslife adopts Org Social. The Relay ships bridges to Mastodon and RSS. Webmentions are integrated into the specification.</li>
</ul>
<h2>From the explosion to maturity</h2>
<p>The autumn of 2025 was the big explosion.</p>
<p>The specification started moving fast, and a good part of the credit goes to proposals from outside. In version 1.1 (that same August 16) I removed the separation between <code>* Posts</code> and the first <code>**</code>, and dropped the <code>:REPLY_URL:</code> property. That change was pushed by <a href="https://github.com/confusedalex" target="_blank">@confusedalex</a>: a stranger who showed up, pointed out it was unnecessary, and was right. Nothing validates an idea more than a stranger opening an issue to improve it.</p>
<p>From there, the specification kept growing with judgment:</p>
<ul>
<li><strong>1.2</strong>: the avatar now had to be at least 128x128 in JPG or PNG.</li>
<li><strong>1.3</strong>: <code>:MOOD:</code> (reactions) and <code>:GROUP:</code> (groups) arrived. Groups were added on September 20.</li>
<li><strong>1.4</strong>: <code>:INCLUDE:</code> to share other people's posts (the classic boost) and <code>:MIGRATION:</code> to announce a domain change.</li>
<li><strong>1.5</strong>: <code>:VISIBILITY:</code> with the value <code>mention</code>, for posts only mentioned people can see.</li>
</ul>
<p>And while the specification grew, so did the ecosystem. Org Social stopped being a file and became a constellation of programs.</p>
<p>Even before the Relay existed, discovering people was a manual affair: a <code>registers.txt</code> file in the repository where people signed up by hand. It reached 17 active users.</p>
<p>The <strong>Relay</strong> was born on September 5 and hit its v1.0 on November 1. It is the piece that indexes users, mentions, replies, groups and threads across the whole network, so a client can discover new people and find out that a stranger has mentioned you. It retired <code>registers.txt</code> and surfaced a bunch of new accounts that were there but nobody could see.</p>
<p>On October 26, with version 2.3, <a href="https://git.andros.dev/org-social/org-social.el">org-social.el</a> landed on <strong>MELPA</strong>. That is the moment the Emacs client became truly stable.</p>
<h3>The Emacs client I had to rewrite from scratch</h3>
<p>Getting to that stable client was not free. It was my big headache.</p>
<p>The first version of <code>org-social.el</code> I wrote in August, quickly, to prove the idea worked. A single file that grew commit by commit: reading feeds, painting the timeline, replying, polls. It worked. But inside it was a snowball. The logic and the interface were tangled together, everything lived in the same place, and every new feature was a fight. When I tried to add threads, groups, notifications and real time, I hit a wall: the architecture could not take any more.</p>
<p>With everything I had learned, I rewrote it. On October 1, 2025 I announced the second version, <code>org-social.el v2.0</code>, with a completely redesigned interface and "the biggest update yet". It changed more than 4,600 lines at once. I split the monolith into modules: the parser on one side, the feeds on another, the interface in its own <code>ui/</code> folder divided into buffers (timeline, thread, profile, groups, search, discover, notifications), the Relay, the real time, the validator, and more. And there I had to apply several optimization strategies to avoid freezes while Emacs processed feeds or subfeeds. The rewrite was a long and painful birth, but it was worth it.</p>
<p>During the process I found a bug in Org Mode itself.</p>
<p>Every Org Social post needs a unique identifier. The original decision, made in the early days, was to use an ISO 8601 timestamp inside the properties drawer. A post looked like this.</p>
<pre><code class="language-org">** 
:PROPERTIES:
:ID: 2025-05-01T12:00:00+0100
:END:
This is my post.</code></pre>
<p>See that empty headline, the <code>**</code> followed by nothing? Well, it is not empty: it must carry a space after it, even though Org Mode's documentation does not say so and is ambiguous. An empty level-two headline needs that trailing space to be valid syntax. Without the space, <code>**</code> stops being a headline. The problem is that a lot of people (myself included) have <code>delete-trailing-whitespace</code> enabled on save, an Emacs command that removes trailing spaces at the end of each line. It is a universally good practice. And it silently wrecked all your posts.</p>
<p>I reported it and contributed a patch, but it was not accepted for backwards-compatibility reasons. Still, the discussion was very interesting.</p>
<p>I was forced to ship a fix in version 1.6, on January 4, 2026. I moved the ID into the headline.</p>
<pre><code class="language-org">** 2025-05-01T12:00:00+0100
This is my post.</code></pre>
<p>Now the identifier lives where nothing can erase it.</p>
<p>In May 2026 I marked the old format as legacy. It is still valid for compatibility, but I recommend the new one and I document the trailing-space trap for anyone who runs into it. I took the chance in that version to also bring in <code>:LOCATION:</code>, <code>:BIRTHDAY:</code>, <code>:LANGUAGE:</code> and <code>:PINNED:</code>.</p>
<p>During these months I wrote several articles around the project: <a href="http://en.andros.dev/blog/734c56f2/why-org-social-is-the-ethical-fediverse-alternative/">Why Org Social is the ethical Fediverse alternative</a>, where I defend the project without hiding its limits, and <a href="http://en.andros.dev/blog/c68f00c3/quick-tutorial-to-get-a-blog-online-from-org-mode-thanks-to-org-social/">Quick tutorial to get a blog online from Org mode thanks to Org Social</a>, where I use the ecosystem to publish a blog with no server.</p>
<p>With the specification settled and the client mature, the same old pending task remained: making it easy for anyone to start, Emacs users or not.</p>
<h3>iOS and host: finally an easy way in</h3>
<p>On November 18, 2025 <strong>host</strong> was born, the hosting service for your <code>social.org</code> with an automatic nick and public URL, very much in tune with the Tilde philosophy. Not everyone has a server to upload a file to, and this solved that at the root. It also opened the door to native clients, since it let you sync your <code>social.org</code> from any device.</p>
<p>To communicate, most of us use the smartphone far more than the desktop. A native iOS client broke that barrier.</p>
<p>On May 11, 2026 the app reached the <a href="https://apps.apple.com/us/app/org-social/id6764415116" target="_blank">App Store</a>. For the first time you could carry your decentralized social network in your pocket, with the interface anyone who has touched a phone expects: posts, replies, threads, polls, groups, scheduled posts and feed export. And all of it with no analytics, no tracking, no telemetry, no third-party SDKs.</p>
<p>The combination of host and the iOS app made Org Social accessible.</p>
<p>The flow became as simple as this:</p>
<ol>
<li>You sign up to host from your phone. Behind the scenes, host gives you a nick and a public URL.</li>
<li>You write in the iOS client and save. The client uploads your <code>social.org</code> to host.</li>
</ol>
<p>Transparent and frictionless.</p>
<p>Of course, this is also compatible with the Emacs client, and it is bidirectional: you can write in Emacs and carry on in iOS, or the other way around.</p>
<p>On April 21, 2026 I published a tiny post, "writing from iOS", from a test build of the app. Seeing it show up in the timeline, coming from an iPhone and not from an Emacs buffer, was one of those moments when a project crosses a line.</p>
<p>In May the app got custom themes and push notifications, and with that I called it done. It went into maintenance mode. Not all software has to grow forever.</p>
<p>That same month version 1.7 of the specification arrived with the <code>:BOT:</code> property, to mark posts generated by bots without cluttering anyone's timeline. With it, for example, you can play a game of chess against a bot from Org Social.</p>
<p>And the year brought signs of maturity. The Relay shipped bridges to follow Mastodon (ActivityPub) accounts and RSS/Atom feeds from any Org Social client, as if they were just more users. A bit later Webmentions were integrated, so that when you link to someone's article they get notified.</p>
<p>Org Social is starting to talk to the rest of the web, not just to itself.</p>
<h2>The present</h2>
<p>A year later, where do we stand?</p>
<p>The picture of the community is that of a niche project, healthy and stable.</p>
<p>The numbers:</p>
<div class="table"><table>
<thead>
<tr>
<th>Metric</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<td>Registered accounts</td>
<td>267</td>
</tr>
<tr>
<td>Indexed feeds</td>
<td>236</td>
</tr>
<tr>
<td>Posts</td>
<td>2,392</td>
</tr>
<tr>
<td>Follows</td>
<td>1,274</td>
</tr>
<tr>
<td>Active groups</td>
<td>5</td>
</tr>
</tbody>
</table>
</div><p>It stays steady at around 19 active accounts a month. Not millions, and it never will be. It is a small community that is still there, month after month.</p>
<p>The specification is at 1.7, though I do not think a new version will come out, at least not in the short term. It feels finished to me.</p>
<p>The official ecosystem is already a dozen pieces:</p>
<ul>
<li><strong><a href="https://git.andros.dev/org-social/org-social">org-social</a></strong>: the specification, the founding document.</li>
<li><strong><a href="https://git.andros.dev/org-social/org-social.el">org-social.el</a></strong>: the Emacs client, today at version 2.14 and beyond.</li>
<li><strong><a href="https://git.andros.dev/org-social/relay">relay</a></strong>: the P2P server that indexes the network.</li>
<li><strong><a href="https://git.andros.dev/org-social/host">host</a></strong>: hosting for your <code>social.org</code> with an automatic nick.</li>
<li><strong><a href="https://git.andros.dev/org-social/ios">OrgSocialKit / ios</a></strong>: native library and client for iOS and macOS in Swift, available on the <a href="https://apps.apple.com/us/app/org-social/id6764415116" target="_blank">App Store</a>.</li>
<li><strong><a href="https://git.andros.dev/org-social/live-preview">live-preview</a></strong> and <strong><a href="https://git.andros.dev/org-social/static-preview">static-preview</a></strong>: social-media-style preview cards for post URLs.</li>
<li><strong><a href="https://git.andros.dev/org-social/web-reading">web-reading</a></strong>: a web timeline viewer, for those who don't use Emacs.</li>
<li><strong><a href="https://git.andros.dev/org-social/push">push</a></strong>: push notifications for the iOS app.</li>
<li><strong><a href="https://git.andros.dev/org-social/rss-bridge">rss-bridge</a></strong>: turns any RSS/Atom feed into Org Social format, so you can follow whatever you want.</li>
<li><strong><a href="https://git.andros.dev/org-social/awesome">awesome</a></strong>: the curated list of clients, relays, libraries and tools.</li>
</ul>
<p>The important stuff already exists.</p>
<h2>The future</h2>
<p>I would love a client for Android and one for the terminal. Neither is trivial nor impossible.</p>
<p>It would also make sense to extend host so it could hold multimedia content, like images and videos. I am still thinking about it.</p>
<p>What I do want is to improve the funding. Each Relay is a node in the network: the more there are, the more robust it becomes. And Apple's developer account does not pay for itself. If you like the project, consider making a donation.</p>
<p>And if you have made it this far, you could leave a comment on your <code>social.org</code> by following this <a href="http://en.andros.dev/blog/ddd78757/quick-tutorial-to-get-started-on-org-social/">tutorial</a>.</p>
<p>See you in the timeline!</p><hr><p>Help me keep writing Every coffee gives me a push toward the next article. <a href="https://ko-fi.com/W7W02LB83">Sure, it's on me!</a></p><p>Send an email to <a href="mailto:comment+article-5fcf45a2@andros.dev">comment+article-5fcf45a2@andros.dev</a> to leave a comment. The subject will be ignored.</p></body></html>]]></content>
        <author>
            <name>Andros Fenollosa</name>
            <uri>http://en.andros.dev/blog/feed/en/emacs/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[James Cherti: Copy-paste without Emacs org-mode or markdown-mode Formatting Bleeding Into Other Buffers]]></title>
        <id>https://www.jamescherti.com/emacs-fix-org-mode-copy-paste-yank-bleed/</id>
        <link href="https://www.jamescherti.com/emacs-fix-org-mode-copy-paste-yank-bleed/"/>
        <updated>2026-08-24T12:39:53.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>When text is copied from an <code>org-mode</code> or <code>markdown-mode</code> buffer and pasted into another Emacs buffer, its visual formatting can sometimes follow it. For instance, text displayed with a particular color, background, or font weight in an Org or Markdown buffer can retain that appearance after being inserted into a Python buffer.</p>



<p>This can be undesirable because the inserted text may then display with the source buffer's <code>face</code> text property instead of relying on the destination buffer's normal font-lock or syntax highlighting.</p>



<h2>Understanding the face text property</h2>



<p>In Emacs, visual formatting can be represented by the <code>face</code> text property. Some major modes, including <code>org-mode</code> and <code>markdown-mode</code>, make extensive use of text properties, including <code>face</code>, to control how text is displayed. When text containing these properties is yanked (pasted), Emacs can preserve them unless they are explicitly excluded.</p>



<h2>Solution 1: Strip faces universally on paste</h2>



<p>Emacs provides the <code>yank-excluded-properties</code> variable for specifying text properties that should not be retained when text is yanked (pasted) into a buffer. To prevent <code>face</code> properties from being carried into the destination buffer, add the following to your configuration:</p>


<pre><span><code class="hljs language-php">(add-to-<span class="hljs-keyword">list</span> <span class="hljs-string">'yank-excluded-properties '</span>face)</code></span></pre>


<p>With this configuration, the <code>face</code> property is removed from the inserted yanked text. The inserted text can therefore be displayed according to the destination buffer's own font-lock and other display rules.</p>



<p>Tradeoff: Once <code>face</code> is included in <code>yank-excluded-properties</code>, yanked text does not retain its <code>face</code> text property. This is generally desirable when the intention is for inserted text to adopt the appearance of its destination buffer, but it may be undesirable for workflows that intentionally use rich text. Examples of workflows and modes where you might want to preserve rich text include <code>enriched-mode</code>, <code>mu4e</code>, or <code>gnus</code>.</p>



<p>For the majority of users, adding <code>'face</code> to <code>yank-excluded-properties</code> is a worthwhile configuration change. This makes copy and paste more predictable and prevents formatting from leaking into unrelated buffers.</p>



<h2>Solution 2: Strip faces per mode on copy</h2>



<p>While appending <code>face</code> to the global <code>yank-excluded-properties</code> variable sanitizes pasted text, it applies universally across Emacs. For workflows that require rich text in specific applications like <code>mu4e</code> or <code>gnus</code>, a global configuration is too aggressive.</p>



<p>An alternative way is to strip the visual formatting at the source during the copy operation, rather than at the destination during the paste operation. This can be achieved by applying buffer-local advice to <code>filter-buffer-substring-function</code>:</p>


<pre><span><code class="hljs language-lisp"><span class="hljs-comment">;; Copy-paste without org-mode or markdown-mode formatting bleeding into other</span>
<span class="hljs-comment">;; buffers. Alternative to: (push 'face yank-excluded-properties)</span>
<span class="hljs-comment">;; URL: https://www.jamescherti.com/emacs-fix-org-mode-copy-paste-yank-bleed/</span>
(<span class="hljs-name">defun</span> my-strip-face-properties-from-string (<span class="hljs-name">string</span>)
  <span class="hljs-string">"Remove visual face properties from STRING."</span>
  (<span class="hljs-name">remove-text-properties</span> <span class="hljs-number">0</span> (<span class="hljs-name">length</span> string)
                          '(face nil font-lock-face nil)
                          string)
  string)

(<span class="hljs-name">defun</span> my-enable-plain-text-copy ()
  <span class="hljs-string">"Strip visual face properties from copied text in the current buffer."</span>
  (<span class="hljs-name">add-function</span> <span class="hljs-symbol">:filter-return</span>
                (<span class="hljs-name">local</span> 'filter-buffer-substring-function)
                #'my-strip-face-properties-from-string))

(<span class="hljs-name">add-hook</span> 'markdown-mode-hook #'my-enable-plain-text-copy)
(<span class="hljs-name">add-hook</span> 'markdown-ts-mode-hook #'my-enable-plain-text-copy)
(<span class="hljs-name">add-hook</span> 'org-mode-hook #'my-enable-plain-text-copy)

<span class="hljs-comment">;; Other modes</span>
<span class="hljs-comment">;; (add-hook 'prog-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'text-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'conf-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'diff-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'help-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'info-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'compilation-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'shell-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'eshell-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'magit-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'dired-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'term-mode-hook #'my-enable-plain-text-copy)</span>
<span class="hljs-comment">;; (add-hook 'vterm-mode-hook #'my-enable-plain-text-copy)</span>
</code></span></pre>


<p>Instead of filtering the text when it is pasted, this solution cleans the text as it is copied, making sure only plain text enters the Emacs clipboard history (the kill ring).</p>
<div class="yarpp yarpp-related yarpp-related-rss yarpp-template-list">

<h3>Related posts:</h3><ol>
<li><a href="https://www.jamescherti.com/emacs-persist-restore-text-scale/">persist-text-scale.el - Persist and Restore the Text Scale</a></li>
<li><a href="https://www.jamescherti.com/fold-outline-indentation-emacs-package/">outline-indent.el - A modern indentation-based folding mode for Emacs</a></li>
<li><a href="https://www.jamescherti.com/bufferfile-el-delete-or-rename-buffer-file-names-with-their-associated-buffers/">bufferfile: Rename, delete, or copy files and update their associated Emacs buffers (including clones and indirect buffers), buffer-local variables, and features that reference the file path, such as Eglot, Dired buffers, and the recentf list</a></li>
<li><a href="https://www.jamescherti.com/emacs-add-todo-keyword-to-new-org-mode-headings/">Configure Emacs org-mode to automatically add the TODO keyword to new Org Mode headings</a></li>
<li><a href="https://www.jamescherti.com/emacs-customize-ellipsis-outline-minor-mode/">Emacs: Customizing the Ellipsis "…" in outline-mode and outline-minor-mode to Use a More Visually Appealing Indicator for Folded Sections, Such as " ▼"</a></li>
<li><a href="https://www.jamescherti.com/emacs-compile-angel-byte-native-compile/">The compile-angel Emacs package: Byte-compile and Native-compile Emacs Lisp libraries Automatically</a></li>
<li><a href="https://www.jamescherti.com/outline-yaml-el-code-folding-outlining-yaml-files/">Emacs: YAML file code Folding and Outlining</a></li>
</ol>
</div>
</body></html>]]></content>
        <author>
            <name>James Cherti</name>
            <uri>https://www.jamescherti.com</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Sacha Chua: 2026-08-24 Emacs news]]></title>
        <id>https://sachachua.com/blog/2026/08/2026-08-24-emacs-news/</id>
        <link href="https://sachachua.com/blog/2026/08/2026-08-24-emacs-news/"/>
        <updated>2026-08-24T12:37:18.000Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
Emacs 31.1 has been released, whee! Also, Divya's post <a href="https://monadicsheep.org/blog/an-introduction-to-canvas-in-emacs.html">An Introduction to Canvas in GNU Emacs</a> is really cool and I'm looking forward to that becoming generally available in Emacs 32. =)
</p>

<ul>
<li>Emacs 31:
<ul>
<li><a href="https://github.com/emacs-mirror/emacs/blob/emacs-31.1/etc/NEWS">Emacs 31.1 is released!</a> (<a href="https://www.reddit.com/r/emacs/comments/1vwziq6/emacs_311_is_released/">Reddit</a>, <a href="https://news.ycombinator.com/item?id=49385296">HN</a>, <a href="https://news.ycombinator.com/item?id=49335485">HN</a>, <a href="https://news.ycombinator.com/item?id=49341172">HN</a>, <a href="https://irreal.org/blog/?p=14021">Irreal</a>)</li>
<li><a href="https://www.masteringemacs.org/article/whats-new-in-emacs-311">Mickey Petersen: What's New in Emacs 31.1?</a> (<a href="https://www.reddit.com/r/emacs/comments/1vx04o6/whats_new_in_emacs_311/">Reddit</a>)</li>
<li><a href="https://www.emacswiki.org/emacs/EmacsThirtyOneHighlights">EmacsWiki&nbsp;: Emacs Thirty One Highlights</a> (<a href="https://social.sdfeu.org/@pkal/117150341168271831">@pkal@social.sdfeu.org</a>)</li>
</ul></li>
<li>Upcoming events (<a href="https://emacslife.com/calendar/emacs-calendar.ics">iCal file</a>, <a href="https://emacslife.com/calendar/">Org</a>):
<ul>
<li>Emacs Berlin: Emacs-Berlin Hybrid Meetup <a href="https://emacs-berlin.org/">https://emacs-berlin.org/</a> Wed Aug 26 1000 America/Vancouver - 1200 America/Chicago - 1300 America/Toronto - 1700 Etc/UTC - 1900 Europe/Berlin - 2230 Asia/Kolkata – Thu Aug 27 0100 Asia/Singapore</li>
<li>EmacsSF (in person): coffee.el in SF <a href="https://www.meetup.com/emacs-sf/events/316144053/">https://www.meetup.com/emacs-sf/events/316144053/</a> Sat Aug 29 1100 America/Los_Angeles</li>
<li>EmacsATX: Emacs Social <a href="https://www.meetup.com/emacsatx/events/316121976/">https://www.meetup.com/emacsatx/events/316121976/</a> Thu Sep 3 1600 America/Vancouver - 1800 America/Chicago - 1900 America/Toronto - 2300 Etc/UTC – Fri Sep 4 0100 Europe/Berlin - 0430 Asia/Kolkata - 0700 Asia/Singapore</li>
<li>M-x Research: TBA <a href="https://m-x-research.github.io/">https://m-x-research.github.io/</a> Fri Sep 4 0800 America/Vancouver - 1000 America/Chicago - 1100 America/Toronto - 1500 Etc/UTC - 1700 Europe/Berlin - 2030 Asia/Kolkata - 2300 Asia/Singapore</li>
</ul></li>
<li>Emacs configuration:
<ul>
<li><a href="https://github.com/laech/emacs-esc">laech/emacs-esc: Emacs escape key to behave like a normal escape key, in both GUI and terminal. · GitHub</a> (<a href="https://techhub.social/@ohmrun/117121414074004844">@ohmrun@techhub.social</a>)</li>
<li><a href="http://yummymelon.com/devnull/simplifying-setup-for-casual.html">Charles Choi: Simplifying Setup for Casual</a> (<a href="https://irreal.org/blog/?p=14027">Irreal</a>)</li>
<li><a href="https://www.jamescherti.com/emacs-why-use-setq-instead-setopt/">Emacs startup: Why setq beats setopt, customize-set-variable, and use-package :custom</a> (<a href="https://www.reddit.com/r/emacs/comments/1vvh0j4/emacs_startup_why_setq_beats_setopt/">Reddit</a>, <a href="https://irreal.org/blog/?p=14034">Irreal</a>)</li>
<li><a href="https://github.com/KallDrexx/emacs-zero-to-ide-journey/blob/main/README.md">My vanilla Emacs to IDE writeup</a> (<a href="https://www.reddit.com/r/emacs/comments/1vwpmz6/my_vanilla_emacs_to_ide_writeup/">Reddit</a>)</li>
<li><a href="https://github.com/dnaeon/elpacman">elpacman – a package manager for the CLI</a> (<a href="https://www.reddit.com/r/emacs/comments/1vrha7y/elpacman_a_package_manager_for_the_cli/">Reddit</a>)</li>
<li><a href="https://codeberg.org/Vaishnav-Sabari-Girish/dotfiles/src/branch/main/emacs/.config/emacs">emacs config - Day 15 of using emacs</a> (<a href="https://www.reddit.com/r/emacs/comments/1vrguka/day_15_of_using_emacs/">Reddit</a>)</li>
<li><a href="https://github.com/benleis1/emacs-init">Weekend init file hacking</a> (<a href="https://www.reddit.com/r/emacs/comments/1vwj83b/weekend_init_file_hacking/">Reddit</a>)</li>
<li><a href="https://outputerror.com/posts/publishing-my-emacs-config/">Publishing My Emacs Config - Output Error</a></li>
<li><a href="https://eugene-andrienko.com/2026-08-20-emacs-mastodon.html">My Emacs configuration (Mastodon)</a> (<a href="https://mastodon.bsd.cafe/@evgandr/117124441383242840">@evgandr@bsd.cafe</a>)</li>
<li><a href="https://joshblais.com/blog/studium-emacs/">Studium Emacs - The Universe of Joshua Blais</a></li>
</ul></li>
<li>Emacs Lisp:
<ul>
<li><a href="https://github.com/mattiase/xr">mattiase/xr: Inverse of rx: convert Emacs string regexps to rx form · GitHub</a> (<a href="https://fosstodon.org/@aethor/117148031030939592">@aethor@fosstodon.org</a>)</li>
</ul></li>
<li>Appearance:
<ul>
<li><a href="https://github.com/berenddeboer/omarchy-emacs-theme">Emacs Omarchy Quattro dynamic theming support</a> (<a href="https://www.reddit.com/r/emacs/comments/1vu2hyw/emacs_omarchy_quattro_dynamic_theming_support/">Reddit</a>)</li>
</ul></li>
<li>TRAMP:
<ul>
<li><a href="https://www.youtube.com/watch?v=suTeZppPG4Y">Emacs Tramp Mode for SSH</a> (00:18)</li>
</ul></li>
<li>Writing:
<ul>
<li><a href="https://www.jamescherti.com/emacs-spell-checker-flyspell-ispell-aspell/">Emacs: Configuring Flyspell, Ispell, and Aspell to Minimize False Positives in Source Code and Prose</a> (<a href="https://www.reddit.com/r/emacs/comments/1vw6mta/emacs_configuring_flyspell_ispell_and_aspell_to/">Reddit</a>)</li>
<li><a href="https://mgallego.gitlab.io/posts/comprobado-de-traduccion-en-espa%C3%B1ol-en-emacs/">Comprobado de Faltas de Ortografía en Emacs - MoiDev - Blog personal de Moises Gallego</a> (<a href="https://social.linux.pizza/@jdrm/117149242955786508">@jdrm@social.linux.pizza</a>)</li>
<li><a href="https://github.com/enricoflor/latex-table-wizard">(not very new) package: latex-table-wizard</a> (<a href="https://www.reddit.com/r/emacs/comments/1vt873b/not_very_new_package_latextablewizard/">Reddit</a>)</li>
<li><a href="https://github.com/laserattack/emado/">emado: manage 100k+ markdown entries from Emacs at native speed</a> (<a href="https://www.reddit.com/r/emacs/comments/1vqtodk/emado_manage_100k_markdown_entries_from_emacs_at/">Reddit</a>)</li>
</ul></li>
<li>Denote:
<ul>
<li><a href="https://mike.hostetlerhome.com/no-graph-needed">No Graph Needed — Where Are The Wise Men?</a> (<a href="https://appdot.net/@mikehoss/117117540392632077">@mikehoss@appdot.net</a>)</li>
<li><a href="https://www.youtube.com/watch?v=Pfv9Bvd8krk">Denote Dynamic Org Block | Advanced</a> (15:02)</li>
<li><a href="https://github.com/SenkiReign/denote-spatial">denote-spatial</a> (<a href="https://www.reddit.com/r/emacs/comments/1vrc7we/denotespatial/">Reddit</a>)</li>
</ul></li>
<li>Org Mode:
<ul>
<li><a href="https://list.orgmode.org/87v791m6be.fsf@localhost">Org Mode requests: [FR] Include :var expansion in expanded noweb references (was: ob-clojure :var header argument not work when src block is noweb called by another src block)</a></li>
<li><a href="https://youtu.be/yEUfHe9nUJo">Easy Research with Emacs and Org Mode</a>  (3:32, <a href="https://www.reddit.com/r/emacs/comments/1vszile/easy_research_with_emacs_and_org_mode/">Reddit</a>)</li>
<li><a href="https://orgmode.org/es/index.html">Org mode para GNU Emacs</a> (<a href="https://activity.andros.dev/@andros/statuses/01M07HVRXF3364BWJQYD15QKTH">@andros@activity.andros.dev</a>)</li>
<li><a href="https://outputerror.com/posts/tracking-reading-in-emacs/">Tracking Reading in Emacs - Output Error</a> (<a href="https://mas.to/@rickwysocki/117113270084212412">@rickwysocki@mas.to</a>)</li>
<li><a href="https://github.com/yibie/org-other-agenda">org-other-agenda: render the same Org agenda data as a board</a> (<a href="https://www.reddit.com/r/emacs/comments/1vuw4lp/orgotheragenda_render_the_same_org_agenda_data_as/">Reddit</a>)</li>
<li><a href="https://nemin.hu/pandoc-org-reader/">Introducing pandoc-org-reader - Nemin's Blog</a> (<a href="https://ohai.social/@nemin/117146134339106453">@nemin@ohai.social</a>)</li>
<li><a href="https://github.com/agzam/pdf-text">pdf text reflow</a> (<a href="https://www.reddit.com/r/emacs/comments/1vwjwyv/pdf_text_reflow/">Reddit</a>) - extract PDF text into Org</li>
<li><a href="https://randyridenour.net/posts/2026-08-17-html-preview-for-org-files.html">Randy Ridenour: HTML Preview for Org Files</a> (<a href="https://irreal.org/blog/?p=14031">Irreal</a>)</li>
</ul></li>
<li>Coding:
<ul>
<li><a href="https://github.com/KallDrexx/emacs-zero-to-ide-journey/">KallDrexx/emacs-zero-to-ide-journey · GitHub</a> (<a href="https://mastodon.social/@jamescherti/117150315500618425">@jamescherti</a>)</li>
<li><a href="https://github.com/agzam/magit-gha-badge.el">GHA Status badge in Magit buffer</a> (<a href="https://www.reddit.com/r/emacs/comments/1vvkia7/gha_status_badge_in_magit_buffer/">Reddit</a>)</li>
<li><a href="https://www.youtube.com/watch?v=jMcrqwRSY18">How to Use HTML Mode in Emacs</a> (12:04)</li>
<li><a href="https://github.com/benleis1/emacs-init/blob/main/lsp-mode.md">I decided to give eglot a try after using lsp-mode for a while.  (Java focused)</a> (<a href="https://www.youtube.com/watch?v=fUnq9nMAEv0">YouTube</a> 07:49, (<a href="https://www.reddit.com/r/emacs/comments/1vs4pqa/i_decided_to_give_eglot_a_try_after_using_lspmode/">Reddit</a>)</li>
</ul></li>
<li>Mail, news, and chat:
<ul>
<li><a href="https://www.reddit.com/r/emacs/comments/1vrk0e5/display_comments_of_a_reddit_post_inside_elfeed/">Display comments of a reddit post inside elfeed</a></li>
</ul></li>
<li>Spacemacs:
<ul>
<li><a href="https://www.youtube.com/watch?v=gwcvqPWDKxg">Spacemacs | Bug fixing &amp; Package updates</a> (07:16, in French)</li>
</ul></li>
<li>Multimedia:
<ul>
<li><a href="https://monadicsheep.org/blog/an-introduction-to-canvas-in-emacs.html">An Introduction to Canvas in GNU Emacs</a> (<a href="https://www.reddit.com/r/emacs/comments/1vwctxc/an_introduction_to_canvas_in_gnu_emacs/">Reddit</a>, <a href="https://lobste.rs/s/9xglf3/introduction_canvas_gnu_emacs">lobste.rs</a>)</li>
<li><a href="https://tv.dyne.org/w/xwT9dCupiB9oMpN9QHB7T9">Canavs Patch is Merged! Long Live Emacs! - Dyne.org TV</a> (<a href="https://mathstodon.xyz/@divyaranjan/117145254110781935">@divyaranjan@mathstodon.xyz</a>)</li>
<li><a href="https://anggtwu.net/2026-eepitch-svg.html">A REPL for learning SVG (with a variant of eepitch; 2026)</a></li>
</ul></li>
<li>Fun:
<ul>
<li><a href="https://codeberg.org/nosrednayduj/moo-el">nosrednayduj/moo-el: Emacs library for connecting to MOO servers. - Codeberg.org</a> (<a href="https://hachyderm.io/@nosrednayduj/117141829407138916">@nosrednayduj@hachyderm.io</a>)</li>
</ul></li>
<li>LLMs:
<ul>
<li><a href="https://www.youtube.com/watch?v=xHEnWvKmSKM">Play your LLM like an instrument (stdin | LLM | stdout)</a> (28:24, <a href="https://www.reddit.com/r/emacs/comments/1vsrewg/stdin_llm_stdout/">Reddit</a>)</li>
<li><a href="https://thanosapollo.org/posts/emacs-agentic-workflow/">Thanos Apollo: My Emacs Agentic Workflow</a></li>
<li><a href="https://donovan-ratefison.mg/2026/08/20/I-smell-Emacs-and-Smalltalk-on-DeepSeek-Harness/">Donovan R.: 💭 I Smell Emacs and Smalltalk on DSH (DeepSeek Harness)</a></li>
<li><a href="https://www.teachmaths.org/20260822-claude-code/">Matt Maguire: AI Agents Take Over Lesson Prep</a></li>
</ul></li>
<li>Community:
<ul>
<li><a href="https://www.reddit.com/r/emacs/comments/1vvhz9z/what_made_you_try_emacs/">What made you try Emacs?</a></li>
<li><a href="https://www.youtube.com/watch?v=Rke2gGCCk70">Prot Asks: Per Nordlöw about his Emacs extensions</a> (01:50:38)</li>
<li><a href="https://sachachua.com/blog/2026/08/20-aout-emacs-chat-abdallah-maouche/">Sacha Chua: Emacs Chat 29: Abdallah Maouche (en français)</a> (<a href="https://www.youtube.com/watch?v=_qkvvVP8ZMQ">YouTube</a>, 58:41)</li>
</ul></li>
<li>Other:
<ul>
<li><a href="https://www.jamescherti.com/emacs-fix-org-mode-copy-paste-yank-bleed/">Emacs: Copy-paste without org-mode formatting bleeding into other buffers | James Cherti</a> (<a href="https://mastodon.social/@jamescherti/117150544255180187">@jamescherti</a>, <a href="https://www.reddit.com/r/emacs/comments/1vx1sbd/emacs_copypaste_without_orgmode_formatting/">Reddit</a>) - face and yank-excluded-properties</li>
<li><a href="https://paste.karthinks.com/e09ee860-warnings-timestamp-mode.el.html">warnings-timestamp-mode.el</a> (<a href="https://hachyderm.io/@mosu/117141571333141539">@mosu@hachyderm.io</a>)</li>
<li><a href="https://git.andros.dev/andros/efinger.el">andros/efinger.el: Finger protocol (RFC 1288) client for Emacs with an Elfeed-inspired reader. - Forgejo: Beyond coding. We forge.</a> (<a href="https://activity.andros.dev/@andros/statuses/01M0S79R7S1887ENQZRJ4EVAKZ">@andros@activity.andros.dev</a>)</li>
<li><a href="https://joshblais.com/blog/i-made-my-phone-emacs/">My phone is now just an emacs interface - The Universe of Joshua Blais</a></li>
</ul></li>
<li>Emacs development:
<ul>
<li>emacs-devel:
<ul>
<li><a href="https://lists.gnu.org/archive/html/emacs-devel/2026-08/msg00707.html">Re: SDL terminal type</a> - limitations of GTK 4</li>
<li><a href="https://yhetil.org/emacs-devel/7ff10d47-3ca2-430b-ab1c-71a73b993f2a@cs.ucla.edu/">Re: current-time-list now defaults to nil in Emacs master - Paul Eggert</a> - notes on convert-time and encode-time</li>
</ul></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=23b0f4bdb989e2caccc255ca59187f4880995317">; * etc/NEWS: Document improved Emoji support on text terminals.</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=449392bddbd1ffe118055c7657e923b3056f149d">viper-ex: Implement list and number commands</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=f856da36cd3b3406891bb81c48542a843424f442">Allow evaluating Eshell forms using lexical binding</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=d5fea6863cc5d95b7af9052c1e7d5347693a6e41">Document Eshell's Lisp pipes feature</a></li>
<li><a href="https://git.savannah.gnu.org/cgit/emacs.git/commit/etc/NEWS?id=8a8d9b5c6a48239b235c9cf5bc4e8816e3520501">Make electric-pair-mode respect field boundaries (bug#50236)</a></li>
</ul></li>
<li>New packages:
<ul>
<li><a target="_blank" href="https://melpa.org/#/auto-capitalize">auto-capitalize</a>: Automatic capitalization with batteries included (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/browser-gt">browser-gt</a>: WebSocket bridge to a Chrome/Firefox extension (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/cypher-ts-mode">cypher-ts-mode</a>: Cypher editing mode (MELPA)</li>
<li><a target="_blank" href="https://elpa.nongnu.org/nongnu/doom-game.html">doom-game</a>: DOOM on Emacs (NonGNU ELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/efinger">efinger</a>: Finger client and .plan feed reader (MELPA)</li>
<li><a target="_blank" href="https://elpa.gnu.org/packages/flymake-harper.html">flymake-harper</a>: Flymake backend for Harper (GNU ELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/gptel-agent-harness">gptel-agent-harness</a>: Autonomous coding-agent harness for gptel-agent (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/guard">guard</a>: Custom modular init framework (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/hel">hel</a>: Helix Emulation Layer (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/minibuffer-frame">minibuffer-frame</a>: Minibuffer in centered child frame (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/mu4e-autotask">mu4e-autotask</a>: Email automation for mu4e (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/peppers-theme">peppers-theme</a>: Custom dark theme based on modus framework (MELPA)</li>
<li><a target="_blank" href="https://elpa.nongnu.org/nongnu/sapling.html">sapling</a>: Magit-like interface for Sapling (NonGNU ELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/schlau-compile">schlau-compile</a>: Git-root-aware interface to `compile' (MELPA)</li>
<li><a target="_blank" href="https://melpa.org/#/shazam">shazam</a>: Shazam Interface (macOS only) (MELPA)</li>
<li><a target="_blank" href="https://elpa.nongnu.org/nongnu/vc-sapling.html">vc-sapling</a>: VC backend for Sapling (NonGNU ELPA)</li>
</ul></li>
</ul>

<p>
Links from <a href="https://www.reddit.com/r/emacs">reddit.com/r/emacs</a>, <a href="https://www.reddit.com/r/orgmode">r/orgmode</a>, <a href="https://www.reddit.com/r/spacemacs">r/spacemacs</a>, <a href="https://mastodon.social/tags/emacs">Mastodon #emacs</a>, <a href="https://bsky.app/hashtag/emacs">Bluesky #emacs</a>, <a href="https://hn.algolia.com/?query=emacs&amp;sort=byDate&amp;prefix&amp;page=0&amp;dateRange=all&amp;type=story">Hacker News</a>, <a href="https://lobste.rs/search?q=emacs&amp;what=stories&amp;order=newest">lobste.rs</a>, <a href="https://programming.dev/c/emacs?dataType=Post&amp;page=1&amp;sort=New">programming.dev</a>, <a href="https://lemmy.world/c/emacs">lemmy.world</a>, <a href="https://lemmy.ml/c/emacs?dataType=Post&amp;page=1&amp;sort=New">lemmy.ml</a>, <a href="https://planet.emacslife.com">planet.emacslife.com</a>, <a href="https://www.youtube.com/playlist?list=PL4th0AZixyREOtvxDpdxC9oMuX7Ar7Sdt">YouTube</a>, <a href="http://git.savannah.gnu.org/cgit/emacs.git/log/etc/NEWS">the Emacs NEWS file</a>, <a href="https://emacslife.com/calendar/">Emacs Calendar</a>, and <a href="https://lists.gnu.org/archive/html/emacs-devel/2026-08">emacs-devel</a>. Thanks to Andrés Ramírez for emacs-devel links. Do you have an Emacs-related link or announcement? Please e-mail me at <a href="mailto:sacha@sachachua.com">sacha@sachachua.com</a>. Thank you!</p>
<div><a href="https://sachachua.com/blog/2026/08/2026-08-24-emacs-news/index.org">View Org source for this post</a></div>
<p>You can <a href="mailto:sacha@sachachua.com?subject=Comment%20on%20https%3A%2F%2Fsachachua.com%2Fblog%2F2026%2F08%2F2026-08-24-emacs-news%2F&amp;body=Name%20you%20want%20to%20be%20credited%20by%20(if%20any)%3A%20%0AMessage%3A%20%0ACan%20I%20share%20your%20comment%20so%20other%20people%20can%20learn%20from%20it%3F%20Yes%2FNo%0A">e-mail me at sacha@sachachua.com</a>.</p></body></html>]]></content>
        <author>
            <name>Sacha Chua</name>
            <uri>https://sachachua.com/blog/category/emacs/feed/index.xml</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Emacs Numbered Backup Strategy]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/08/emacs-numbered-backup-strategy.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/08/emacs-numbered-backup-strategy.html"/>
        <updated>2026-08-12T23:01:35.675Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>Emacs produces a backup of each file by default.  It does this by
  appending a tilde to the file's name.  For example, <code>Myfile.txt</code> is backed up to
  <code>Myfile.txt~</code>.  It's a reasonable strategy, assuming that
  a Version Control system is in place.</p>

<p>But in the absence of version control, you're limited to restoring
  just the previous version; older versions need to be restored from
  backup.</p>

<p>I use git for coding projects that I share to the public.  But I
  didn't create a repository for my home directory.  So when
  <code>C-c a m TAG</code><sup>1</sup> generated an error instead of a list of
  TAGged items, I hoped fervently that the latest init file change was
  to blame.</p>

<p>After finding and fixing the problem quickly, (and realizing I was
  lucky this time), I resolved to be more diligent about disaster
  recovery.  I looked into what backup schemes Emacs already
  provides.</p>

<p>Emacs can create unique backups for a file rather than overwriting
  the previous backup.  When Emacs sees a numbered backup for a given
  file, it will make a new, sequentially-named file for each backup of
  that file, while continuing to use the "append tilde" method for
  other files.</p>

<p>Once I renamed (or copied) <code>.emacs~</code>
  to <code>.emacs.~1~</code>, Emacs created backups
  named <code>.emacs.~2~</code>, <code>.emacs.~3~</code> (and so on).
  I didn't even need to change a variable -- when Emacs
  detected <code>.emacs.~1~</code>, it knew that backups
  of <code>.emacs</code> needed to be numeric, so it acted
  accordingly.  Emacs continues to use <code>bills.org~</code> as the backup
  for <code>bills.org</code>.  Until I tell it otherwise.</p>
<div class="separator"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMqpJ_S2fJ68hZ6CYvyND36nEBMkLOjTC0VPpaiz0wxgmWkzlZf8UtU6DppwtDOhZOxx4d1J8_LBOJSl1RETPzAqFMZbeMMczcpc1aUzKdhHDqjEoFpr_2xK_883vE8e4rmcaxDRvhXqrFvsIukotKzMhN9_u2a2CE_VCdzPD7F5rGDew0_8tH20LeNVA/s143/init-file-numbered-backups.gif"><img alt="A list of file names in light grey text on a blue background: .emacs; .emacs~1~; .emacs~2~; .emacs~3~;" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMqpJ_S2fJ68hZ6CYvyND36nEBMkLOjTC0VPpaiz0wxgmWkzlZf8UtU6DppwtDOhZOxx4d1J8_LBOJSl1RETPzAqFMZbeMMczcpc1aUzKdhHDqjEoFpr_2xK_883vE8e4rmcaxDRvhXqrFvsIukotKzMhN9_u2a2CE_VCdzPD7F5rGDew0_8tH20LeNVA/s200/init-file-numbered-backups.gif"></a></div>
<p>Read
  the <a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Backup-Names.html" target="_blank">Emacs Manual to learn more about numbered backups</a>.</p>

<hr><p><sup>1</sup>  <code>C-c a m</code> invokes <code>org-tags-view</code></p>
<p>The default values for the pertinent backup variables are:
</p><ul>
  <li><code>make-backup-files</code> is t</li>
  <li><code>backup-by-copying</code> is nil</li>
  <li><code>version-control</code> is nil</li>
</ul>
<p></p>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: How Much Management Does Knowledge Need?]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/08/how-much-management-does-knowledge-need.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/08/how-much-management-does-knowledge-need.html"/>
        <updated>2026-08-10T22:19:40.478Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
The topic of this month's Emacs Carnival interests me, even though I
"dump ... notes into a directory and simply grep for information
...."<sup>0</sup> Org does not work the way my brain
works,<sup>1</sup> or rather, my brain doesn't work the way Org works.
</p>

<p>
I do use Org.  At first I created a few Org files (work.org
and home.org, and maybe todo.org) thinking I could rigidly dedicate
all my topics to them.
</p>

<p>
Over time I had about dozen files, one for nearly every major facet of my
life until I gave up.  Why?  Because it really doesn't matter where I
put Org TODOs and their notes.  When I need to locate something, Org
provides a way to search entries for a regular expression, which is
what <code>grep</code> does, sans all that Unixy CLI stuff.
</p>

<p>
So faced with the task of paying one of my wife's medical bills, I
could agonize over whether to put the task into <code>wife.org</code> or
<code>bills.org</code> or <code>medical.org</code>.<sup>2</sup> Or I
could just:
</p>

<ul>
<li>establish the task somewhere,</li>
<li>act on it</li>
<li>link to a receipt</li>
<li>mark it done<sup>3</sup></li>
</ul>

<p>
I created a special template for <code>org-capture</code>, which places these
one-off tasks into <code>todo.org</code>.  And it clocks into the task, too.
</p>

<p>
A topic with even greater complexity is "education."  I'm a lifelong
learner; my <code>edu.org</code> file has (only) 29 headings, all of
which have at least a few subheadings.  If I learn about an Emacs
feature, I might want to write about it, so I'll put a task
into <code>blog.org</code> that links to it.
</p>

<p>
Occasionally a post will span multiple topics in <code>edu.org</code>,
topics such as Emacs, Vivaldi, LibreOffice, HTML or LaTeX.  In a year
from now, which file should I look for it in?  The keystroke <code>C-c
a s</code> comes to the rescue.<sup>4</sup> I might search for "Sacha"
because all I remember is that I discovered an Emacs feature on
Sacha's website.<sup>5</sup> Incidentally, I get 13 hits when I
search for "Sacha," one of which was the one I had in mind.
</p>

<p>
The question I think all Emacs users face is, "At what point does
tweaking Emacs (and self-quantifying tools in general) tip the scales from
<em>aiding</em> our progress to <em>impeding</em> it?"  I dread going down
a Knowledge Management rabbit hole,<sup>6</sup> but I'm sure I'll enjoy
the leap.
</p>

<p>
  <sup>0</sup> 
<a href="https://www.chiply.dev/post-august-emacs-carnival/#:~:text=dump%20their%20notes%20into%20a%20directory%20and%20simply%20grep%20for%20information" target="_blank">Charlie
  Holland's Search for Knowledge</a>
</p>

<p>
  <sup>1</sup> <a href="https://www.gnu.org/software/emacs/manual/html_mono/org.html#Top" target="_blank">A tree works like your brain.</a>
</p>

<p>
  <sup>2</sup> Don't even get me started on insurance.
</p>

<p>
  <sup>3</sup> All of this occurs while clocked in to the task.
</p>

<p>
  <sup>4</sup> In this case, <code>C-c a</code> is bound to <code>org-agenda</code>.
</p>

<p>
  <sup>5</sup>  <a href="https://sachachua.com/blog/" target="_blank">Sacha Chua's
    blog</a>
</p>
<p>
  <sup>6</sup> Can one gain knowledge of Knowledge Management Systems
without having a Knowledge Management System in which to record that
knowledge?  That may be a Zen koan for someone.
</p>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Vakana -- Sneak a Peek]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/08/vakana-sneak-peek.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/08/vakana-sneak-peek.html"/>
        <updated>2026-08-09T23:55:27.267Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
  I installed Donovan Ratefison's brand-new Vakana app<sup>1</sup> on my Android
device this morning.  After following the tutorial, I exported the
thread as an Org-mode-compatible package in a ZIP and emailed it to
myself in order to transfer it to my desktop.
</p>

<p>
After opening the Org file that Vakana generated, I exported the
entire buffer to HTML.  The result is shown below.  The only change
I wanted to make was to scale the bead of each heading 25%.<sup>2</sup>
</p>

<p>
It's fortuitous that Donovan Ratefison released Vakana in a month
whose Emacs Carnival theme is "The Search for Knowledge," because
Vakana provides a unique way to manage one's knowledge.  I'll need
to become proficient in it before adding it to my workflow.  So my
eventual Emacs Carnival submission will be more pedestrian.</p>

<hr>
<p><sup>1</sup> Learn about
  Vakana <a href="https://donovan-ratefison.mg/2026/08/09/Vakana-mg-mobile-app-is-now-available/" target="_blank">here</a>
  or download <a href="https://vakana.mg/" target="_blank">here</a>.</p>
<p><sup>2</sup> The images used for beads are SVG files.  However this
  blogging platform does not support that file type, so I converted
  them to GIF for this post.  This should explain why they don't look awesome.</p>

<blockquote>
  


Your first thread



<div class="content">
<h1>Your first thread
<br>
<span class="subtitle">A 90-second tour. Walk it bead by bead, then make it yours — delete it, or reload it anytime from Settings → Pro Tips.</span>
</h1>
<div>
<h2>Table of Contents</h2>
<div>
<ul>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#org368a648">1. The thread</a></li>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#org6e2b42d">2. Start here · a 90-second tour</a></li>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#org84cb1bc">3. 1/5 · Tap the bead</a></li>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#orgfbaa551">4. 2/5 · Open the thread</a></li>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#org1bc1c79">5. 3/5 · Comment — swipe me right</a>
<ul>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#org4d07f64">5.1. Comments</a>
<ul>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#orga4bedc1">5.1.1. Comment — <span class="timestamp-wrapper"><span class="timestamp">[2026-08-09 Sun 18:50]</span></span></a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#org3423051">6. 4/5 · Long-press a bead in the list</a>
<ul>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#org706a014">6.1. Comments</a>
<ul>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#org0f1c182">6.1.1. Comment — <span class="timestamp-wrapper"><span class="timestamp">[2026-08-09 Sun 18:22]</span></span></a></li>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#org5d7a4c9">6.1.2. Comment — <span class="timestamp-wrapper"><span class="timestamp">[2026-08-09 Sun 19:14]</span></span></a></li>
</ul>
</li>
</ul>
</li>
<li><a href="https://ray-on-emacs.blogspot.com/search/label/Emacs#org5f732e6">7. 5/5 · Close it, then make it yours</a></li>
</ul>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">1.</span> The thread</h2>
<div class="outline-text-2">

<div class="figure">
  <div class="separator"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1tF_ilrlbCNsg9tOiYnT-pg_Jr868suY4oZNDc7xKRG7oIRZuQykgS85a-nkkaNLaz9DsUIHvxevmyWsvDe1UM3boaF_tP21ZUjSKbDkHf1eIIciI8zNgtsywu3ty7h2ZA4SQAkIstEVlsMJ1HFyzZKZJrro2DBC8sa1B-8I-meJHlvV1DuDbgphTXK4/s1144/chain.gif"><img alt="" width="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1tF_ilrlbCNsg9tOiYnT-pg_Jr868suY4oZNDc7xKRG7oIRZuQykgS85a-nkkaNLaz9DsUIHvxevmyWsvDe1UM3boaF_tP21ZUjSKbDkHf1eIIciI8zNgtsywu3ty7h2ZA4SQAkIstEVlsMJ1HFyzZKZJrro2DBC8sa1B-8I-meJHlvV1DuDbgphTXK4/s200/chain.gif"></a></div>

</div>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">2.</span> Start here · a 90-second tour</h2>
<div class="outline-text-2">
<p>
<span class="timestamp-wrapper"><span class="timestamp">[2026-08-09 Sun]</span></span>
  
  </p><div class="separator"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnHJVEzKRHbImtYr8IccKtnxpRTNwuahPS67F5lng3XWE9X5zuYL0Or-O4XGARtMCTmI9V_9SnsLMaBTiprgcbGQxFlytrtmCwScFdV_c257eBB3eRLcpYkLf9FW18pGJGTBv4w2DtGFvDBk9eTpCk-xeNtrd5fiDex0UZrDuNBJvyX8qAipRRVrdLYug/s259/bead-1.gif"><img alt="First Bead, Marked 0" width="64" height="64" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnHJVEzKRHbImtYr8IccKtnxpRTNwuahPS67F5lng3XWE9X5zuYL0Or-O4XGARtMCTmI9V_9SnsLMaBTiprgcbGQxFlytrtmCwScFdV_c257eBB3eRLcpYkLf9FW18pGJGTBv4w2DtGFvDBk9eTpCk-xeNtrd5fiDex0UZrDuNBJvyX8qAipRRVrdLYug/s200/bead-1.gif"></a></div>
Welcome — and if this is all new, that’s perfectly fine. We’ll go gently.
<p></p>

<p>
<i>Vakana</i> is the Malagasy word for <b>beads</b> — there’s no other word for them in the language, and it’s where this app takes its name. The idea is old and simple: you turn a moment into a small <b>bead</b> — a little coloured token — and beads you keep together form a <b>thread</b> you can walk back along, like beads on a string. (These six notes are already a thread — <i>“Your first thread.”</i>)
</p>

<p>
That’s the whole shape of it. What a bead <i>means</i> is always yours — the app never decides that for you.
</p>

<p>
This is a short, hands-on tour: five steps, each one a thing you try yourself.
</p>

<p>
Next → <b>1/5 · Tap the bead</b>.
</p>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">3.</span> 1/5 · Tap the bead</h2>
<div class="outline-text-2">
<p>
<span class="timestamp-wrapper"><span class="timestamp">[2026-08-08 Sat]</span></span>
  </p><div class="separator"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKsxL3b4Yvdo5DeRvpqSJuHKNLnJTsodAnswtHtZ5giJkDvlc5LayXRNKrh-gApgzIb_oXQFOD_CUQ56NJSO7sHF3bM5TXlMGVPm-9nE2ZOeipa0IE01DIeMBm82pdjZWr7k39KnV5Dis9wji1_GSH-TYQeZvuu3XxrbX0wW-reFfoYgCMeiY7ZU6I35w/s517/bead-2.gif"><img alt="Second Bead, Marked 1" width="64" height="64" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKsxL3b4Yvdo5DeRvpqSJuHKNLnJTsodAnswtHtZ5giJkDvlc5LayXRNKrh-gApgzIb_oXQFOD_CUQ56NJSO7sHF3bM5TXlMGVPm-9nE2ZOeipa0IE01DIeMBm82pdjZWr7k39KnV5Dis9wji1_GSH-TYQeZvuu3XxrbX0wW-reFfoYgCMeiY7ZU6I35w/s200/bead-2.gif"></a></div>

,*Tap the bead* at the top of this note — the composer opens. That’s where you pick a bead’s <b>shape</b>, <b>pattern</b>, <b>mood-colour</b> and a tiny <b>glyph</b> (this one’s is the number 1️⃣).
<p></p>

<p>
Eight shapes, eight patterns, eight moods — small enough to learn, wide enough for anything. There’s no legend: a circle isn’t “an encounter” unless <i>you</i> decide it is.
</p>

<p>
How does the user navigate the beads?
Now I see "&lt; Prev" and "Next &gt;" so it's obvious.
</p>

<p>
Next → <b>2/5 · Open the thread</b>.
</p>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">4.</span> 2/5 · Open the thread</h2>
<div class="outline-text-2">
<p>
<span class="timestamp-wrapper"><span class="timestamp">[2026-08-07 Fri]</span></span>
  </p><div class="separator"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYM_6pny9x7S_BR1RzQ_caB7VuyPVjEzDmNZJWgpWl4m113e-cSlKJUVei9nF5wMk52MLzOrQzo999M04MsRuRDATM1pHAG0iay2Y5-RnyX6AV9HrRpcRTTrMq1HmNlVtexnhXXodgna-JuiqfO7_WhlWzu6A3jp2l8Vxk0EUQZsxd8bAIEeKpkL5mfyo/s516/bead-3.gif"><img alt="Third Bead, Marked 2" width="64" height="64" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYM_6pny9x7S_BR1RzQ_caB7VuyPVjEzDmNZJWgpWl4m113e-cSlKJUVei9nF5wMk52MLzOrQzo999M04MsRuRDATM1pHAG0iay2Y5-RnyX6AV9HrRpcRTTrMq1HmNlVtexnhXXodgna-JuiqfO7_WhlWzu6A3jp2l8Vxk0EUQZsxd8bAIEeKpkL5mfyo/s200/bead-3.gif"></a></div>
You’re on a <b>thread</b> right now — a chain of beads strung together on purpose. <b>Open <b>“Your first thread”</b></b> (tap its chip here, or find it in the Threads view) and you’ll see all six beads in a row you can walk, bead by bead.
<p></p>

<p>
The app also keeps a quiet thread for <b>every day</b> you write, under <i>Days</i> in the Threads view — built for you, no effort.
</p>

<p>
Next → <b>3/5 · Comment</b>.
</p>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">5.</span> 3/5 · Comment — swipe me right</h2>
<div class="outline-text-2">
<p>
<span class="timestamp-wrapper"><span class="timestamp">[2026-08-06 Thu]</span></span>
  
  </p><div class="separator"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzfDjWhFrEz-UH1rEAErUVZZG3C3NVW3WhOfAjDrCddI5qbMVLnVVpymRHa6RiERiVMfxRe5DKnG2TD5JojSHr1vuykh_Ll9CNs_5j4bvcm0xfbXt4VjSThlxWpf7ed9ZXhV72nSNNhx1GdirG5rK_CkGGY4Qcwzm0Wxxiru1x1mCrPzLpiA4UOF0F5NY/s516/bead-4.gif"><img alt="" width="64" height="64" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzfDjWhFrEz-UH1rEAErUVZZG3C3NVW3WhOfAjDrCddI5qbMVLnVVpymRHa6RiERiVMfxRe5DKnG2TD5JojSHr1vuykh_Ll9CNs_5j4bvcm0xfbXt4VjSThlxWpf7ed9ZXhV72nSNNhx1GdirG5rK_CkGGY4Qcwzm0Wxxiru1x1mCrPzLpiA4UOF0F5NY/s200/bead-4.gif"></a></div>
Some thoughts arrive <i>after</i> the moment. Instead of rewriting a note, leave a <b>comment</b> in its margin.
<p></p>

<p>
Try it: tap the <b>💬 at the top</b> of this note — or, back in your list, <b>swipe this row to the right</b>. Either opens the margin, where you can jot a thought or even set it to remind you later.
</p>

<p>
(The next bead, 4️⃣, already has a comment — spot the 💬 on its row.)
</p>

<p>
Next → <b>4/5 · Long-press the bead</b>.
</p>
</div>
<div class="outline-3">
<h3><span class="section-number-3">5.1.</span> Comments</h3>
<div class="outline-text-3">
</div>
<div class="outline-4">
<h4><span class="section-number-4">5.1.1.</span> Comment — <span class="timestamp-wrapper"><span class="timestamp">[2026-08-09 Sun 18:50]</span></span></h4>
<div class="outline-text-4">
<p>
The tutorial shows an icon for comment as a white-filled rectangular balloon with three dots. However the actual icon is a balloon that's an empty circle
</p>
</div>
</div>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">6.</span> 4/5 · Long-press a bead in the list</h2>
<div class="outline-text-2">
<p>
<span class="timestamp-wrapper"><span class="timestamp">[2026-08-05 Wed]</span></span>
  
</p><div class="separator"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9eugmNGNmFg6yLGBfDA6O4proZ6gBFOXHc4jCHDcajIghIXVwcTYR3aULFVYj9YRB9mimGZrZ3k5nIRFHOp8KinbEbP27vq2nN0LOtVWvRNL-CbKBLIznsClKQYpHrdgkHzK7crAdFAxth5oi6ez6ngERg5fdnfrzFJGzfqtj25cMlxitH91J7UPujhM/s517/bead-5.gif"><img alt="Fifth Bead, Marked 4" width="64" height="64" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9eugmNGNmFg6yLGBfDA6O4proZ6gBFOXHc4jCHDcajIghIXVwcTYR3aULFVYj9YRB9mimGZrZ3k5nIRFHOp8KinbEbP27vq2nN0LOtVWvRNL-CbKBLIznsClKQYpHrdgkHzK7crAdFAxth5oi6ez6ngERg5fdnfrzFJGzfqtj25cMlxitH91J7UPujhM/s200/bead-5.gif"></a></div>  
Beads repeat, and you’ll want to find a bead’s kin. This is a thing you do from your <b>list</b>, not from inside a note — so head back to the list first.
<p></p>

<p>
There, <b>press and hold any bead</b>: the list instantly filters to every bead like it. (Long-press is the move most people miss.)
</p>

<p>
Want finer control? Tap the <b>funnel</b> at the top of the list to filter by shape, mood, pattern or glyph, then gather the results into a brand-new thread.
</p>

<p>
Next → <b>5/5 · Make it yours</b>.
</p>
</div>
<div class="outline-3">
<h3><span class="section-number-3">6.1.</span> Comments</h3>
<div class="outline-text-3">
</div>
<div class="outline-4">
<h4><span class="section-number-4">6.1.1.</span> Comment — <span class="timestamp-wrapper"><span class="timestamp">[2026-08-09 Sun 18:22]</span></span></h4>
<div class="outline-text-4">
<p>
I keep coming back to this one — long-pressing a bead is the move most people miss.
</p>
</div>
</div>
<div class="outline-4">
<h4><span class="section-number-4">6.1.2.</span> Comment — <span class="timestamp-wrapper"><span class="timestamp">[2026-08-09 Sun 19:14]</span></span></h4>
<div class="outline-text-4">
<p>
The alarm clock icon next to the note field is for a reminder, not a timestamp. It puts it on the next business day at 9:00 a.m., perhaps.  I pressed it at 3:00 p.m. on Sunday August 9th, and then entered the reminder for the next day at 9:00 a.m..
</p>
</div>
</div>
</div>
</div>
<div class="outline-2">
<h2><span class="section-number-2">7.</span> 5/5 · Close it, then make it yours</h2>
<div class="outline-text-2">
<p>
<span class="timestamp-wrapper"><span class="timestamp">[2026-08-04 Tue]</span></span>

</p><div class="separator"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk3tDt0SyQafFopzLZrfwEbOjSoL-8_XjGktHLKI0go-XWS0ml539xZzN1FtCvVt86-CKQ_DBjNd_mTkowCljLDwf0ZMLRiZFg0s0xE1phswdozWUkG55XSisvQfLP24T9hszq6rjooOzEWL0Uuf6bLZZbrvTNAd_qTontZQmfG13H56vFY_G-aOjI85c/s517/bead-6.gif"><img alt="Sixth Bead, Marked 5" width="64" height="64" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgk3tDt0SyQafFopzLZrfwEbOjSoL-8_XjGktHLKI0go-XWS0ml539xZzN1FtCvVt86-CKQ_DBjNd_mTkowCljLDwf0ZMLRiZFg0s0xE1phswdozWUkG55XSisvQfLP24T9hszq6rjooOzEWL0Uuf6bLZZbrvTNAd_qTontZQmfG13H56vFY_G-aOjI85c/s200/bead-6.gif"></a></div>This thread is <b>closed</b> — notice the clasp. Closing marks a loop that finished; you can reopen it anytime.
<p></p>

<p>
A finished thread can leave the app: open it and <b>Export</b> to turn it into a web page or a slide deck.
</p>

<p>
That’s the whole grammar — <b>bead, thread, comment, close, export</b>. Now make it yours:
</p>

<p>
• <b>Delete this tour</b> — swipe any note left, or remove it in one tap from <b>Settings → Pro Tips → Interactive tutorial</b>.
• Want it back later? <b>Reload</b> from the same place.
</p>

<p>
Now write your first real moment, and bead it however <i>you</i> like.
</p>
</div>
</div>
</div>
<div class="status">
<p>Created: 2026-08-09 Sun 16:42</p>
<p><a href="https://validator.w3.org/check?uri=referer">Validate</a></p>
</div>
</blockquote>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Dedicate an Emacs Window to Its Buffer]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/08/dedicate-emacs-window-to-its-buffer.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/08/dedicate-emacs-window-to-its-buffer.html"/>
        <updated>2026-08-06T17:43:24.706Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
Before I start writing in Org, I dedicate the window.  This "protects"
the content, preventing it from being replaced by something else.  It
ensures I can stay focused on my writing no matter what other buffers
Emacs opens.
</p>

<p>
My usual workflow is to display help on the functions I write about.
Emacs opens the help buffer in another window.  That's good.  But then
if I click on the link to the source code that help references,
<em>that</em> file will open in my writing window, and I lose focus.  Dedicating
the window prevents this.
</p>

<p>
  I <em>could</em> dedicate a window manually by invoking
<code>toggle-window-dedicated</code> (bound to <code>C-x w d</code>)
every time I start writing.  But because I clock in to anything I'm
serious about, I let <code>org-clock-in-hook</code> to do it for me as
shown below.  Note that the hook calls <code>set-window-dedicated-p</code> rather than
<code>toggle-window-dedicated</code> because the latter will
un-dedicate a window that's already dedicated.  I could
configure <code>org-clock-out-hook</code> to un-dedicate the window;
I'm debating whether it's necessary.
</p>

<div class="org-src-container">
<pre>(add-hook 'org-clock-in-hook (<span>lambda</span> () (set-window-dedicated-p nil t)))
</pre>
</div>

<p>
Interestingly, while I was writing this, JTR published his
"Gems" article in which he writes about setting
<code>help-window-keep-selected</code> to true "to keep help in its
own dedicated window, so it won’t open in a separate window once we
follow a link."<sup>1</sup> Perhaps that’s his approach to the same issue.
</p>
<hr>
<p><sup>1</sup>
<a href="https://taonaw.com/2026/08/05/emacs-config-gems-part.html/#:~:text=help-window-keep-selected%20allows%20us%20to%20keep%20help%20in%20its%20own%20dedicated%20window" target="_blank">Emacs Config Gems - Part 3</a></p>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: One Space or Two?]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/08/one-space-or-two.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/08/one-space-or-two.html"/>
        <updated>2026-08-01T23:39:31.648Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>HTML enforces the use of a single space to separate sentences.  If
  you want to add another space, you need to use
  <code>&amp;nbsp;</code>.  I still remember how annoyed I was about
  that.  Back in the century in which I took my technical writing
  course, two spaces were required.  Of course, we weren't using
  computers very often; hand-written sentences needed to appear more
  separated than the words within.</p>

<p>I'm definitely not here to convince anyone to switch.  Instead, I
just want to share that I paid close attention to this detail when I
was formulating my style guide for this blog.  Should I continue with
my out-of-date two-space habit?  Or should I adopt the current
convention?</p>

<p>I turned to Emacs itself for guidance.  It turns out that the
individuals who wrote Emacs comments, docstrings and help text used
two spaces.  Although many times they started each sentence on a new
line.</p>

<p>It turns out that the content here mostly is one-space.  I start
  out using two spaces in Org where most of this originates.  But when
  I export to HTML, that second space vaporizes.  That would seem like a bug to me.</p>

<p>The only place where you'll see evidence of my two-space style is in the comments
  and docstrings of my source code.</p>

<p>Am I the only person who nit picks about such details?</p>

<hr>
<p>Thanks to <a href="https://irreal.org/blog/?p=13979" target="_blank">Irreal for highlighting</a>
  a <a href="https://bicycleforyourmind.com/much-ado-about-emacs-015/#:~:text=Two%20Spaces%20Turn%20Into%20a%20Period%20and%20Space" target="_blank">customization from macosguru</a></p>
  
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: How to Copy the Link at Point -- an Undocumented Feature in EWW]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/07/how-to-copy-link-at-point-undocumented.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/07/how-to-copy-link-at-point-undocumented.html"/>
        <updated>2026-07-24T19:27:24.920Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><h1></h1>
<p>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.</p>
  
<p>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 <code>shr-maybe-probe-and-copy-url</code>
  (bound to <code>u</code> and <code>w</code>
  in <code>eww-mode</code>) does the same thing.  Just position point
  on a link and invoke it.</p>

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


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

<p>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.</p>

<p>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 <code>w</code> key press.  Suppose a
  user expects to copy the page URL and gets the URL of a link, instead?</p>

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

<sup>1</sup>  <a target="_blank" href="https://www.gnu.org/software/emacs//manual/html_node/eww/Basics.html#:~:text=Pressing%20w%20when%20point%20is%20on%20a%20link%20will%20call">GNU Emacs EWW Basic Usage</a>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Modeling Browser History in Emacs]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/07/modeling-browser-history-in-emacs.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/07/modeling-browser-history-in-emacs.html"/>
        <updated>2026-07-21T21:36:54.454Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>If there were an underappreciated web browser feature, it would be
  this: history.</p>

<p>On many browsers, history is invoked with <strong>C-h
  (Ctrl+h)</strong>.  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.<sup>1</sup> </p>

<p>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.</p>

<p>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.</p>

<p>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.</p>

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

<p>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 <code>find-file-hook</code>
  and <code>after-save-hook</code>:<sup>3</sup></p>

<div class="org-src-container">
<pre>(find-file-hook
 '(<span>lambda</span> nil
     (message <span>"%s: Loaded %s"</span>
              (format-time-string <span>"%Y-%m-%d %H:%M:%S"</span> (current-time))
              (buffer-file-name))))
(after-save-hook
 '(<span>lambda</span> nil
    (message <span>"%s: Saved %s"</span>
             (format-time-string <span>"%Y-%m-%d %H:%M:%S"</span> (current-time))
             (buffer-file-name))))
</pre>
</div>

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

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

<p>Then I clock in and change the start time to 12:33.<sup>4</sup></p>

<p>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.</p>

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

<p>What methods do you use to track project time?</p>

<hr>

<p><sup>1</sup> The user also can view history as a simple list in
  reverse chronological order.  And the search can span the entire
  history, if desired.</p>
<p><sup>2</sup> <a target="_blank" href="https://doc.endlessparentheses.com/Fun/savehist-mode.html">https://doc.endlessparentheses.com/Fun/savehist-mode.html</a></p>
<p><sup>3</sup> Note that these expressions are arguments to
  <code>custom-set-variables</code>.</p>
<p><sup>4</sup> 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
  <code>org-clock-rounding-minutes</code> 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.</p>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Emacs Tip Of The Day in a Popup Frame]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/07/emacs-tip-of-day-in-popup-frame.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/07/emacs-tip-of-day-in-popup-frame.html"/>
        <updated>2026-07-19T22:29:30.872Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>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.<sup>1</sup></p>

  <p>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 <code>call-process-shell-command</code>
  to run a one-line batch file<sup>2</sup> that contains this:</p>

  <div class="org-src-container">
<pre><span>"path_to_emacs\emacs.exe"</span> -Q -g 80x42-2+2 -l totd.el --eval (ztotd)
</pre>
</div>

  

  <p>The <code>-l</code> switch loads totd.el, defining
  both <code>totd</code> and <code>ztotd</code>.  The
  <code>--eval</code> switch then invokes <code>ztotd</code>.  (The
  other function, <code>totd</code> is not used; it's retained for reference.)
  The <code>-Q</code> switch starts Emacs "quietly."  <code>-g</code>
  defines window geometries:<sup>3</sup> <abbr>WxH+X+Y</abbr>,
  where</p>
  <ul>
    <li><abbr>W</abbr> and <abbr>H</abbr> specify the frame's width
    and height in character units.</li>
    <li><abbr>X</abbr> and <abbr>Y</abbr>, if ≥0, specify the pixel
    coordinates of the upper left corner of the window.  If &lt;0, specify
    the distance from the right and bottom of the screen. </li>
  </ul>

<p>This is the statement in my init file that runs the batch file:</p>
<div class="org-src-container">
<pre>(call-process-shell-command <span>"cmd.exe /Q /D /C invoke-emacs-totd.bat"</span> nil 0)
</pre>
</div>


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

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

(<span>require</span> '<span>cl-lib</span>)

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

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

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

<p><code>totd</code> 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
  <code>call-process-shell-command</code> instead
  of <code>shell-command</code> to prevent a shell output window from
  appearing in the main frame.<sup>5</sup></p>
<p>
  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.</p>
<p> 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
  <code>image-dired-delete-tag</code> actually <em>do</em>?" you might
  wonder.  I think that's the whole point of a TOTD -- to inspire
  wonder!</p>
<p>
  If you try this on a non-Windows system, please let me know whether
  it works.</p>
<hr>
<p><sup>1</sup> I tried <code>display-buffer-pop-up-frame</code>.
  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.</p>
<p>
  <sup>2</sup> In fact the batch file contains several lines of comments, too, as
  well as the cherished <code>@echo off</code> statement at the very beginning.
  <code>@echo off</code> prevents the many lines of comments from getting dumped to
  standard output; otherwise those comments would end up in a shell buffer.</p>
<p>
  <sup>3</sup> For more information on command line switches for Emacs, see:
  <a target="_blank" href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Invocation.html">https://www.gnu.org/software/emacs/manual/html_node/emacs/Emacs-Invocation.html</a></p>
<p>
  <sup>4</sup> Please see <a target="_blank" href="https://www.emacswiki.org/emacs/TipOfTheDay">https://www.emacswiki.org/emacs/TipOfTheDay</a></p>
<p>
  <sup>5</sup> Thanks to Jackson Ray Hamilton for this suggestion. <a target="_blank" href="https://stackoverflow.com/a/22982525">https://stackoverflow.com/a/22982525</a></p>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: One Hundred Times the Highest Priority]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/07/one-hundred-times-highest-priority.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/07/one-hundred-times-highest-priority.html"/>
        <updated>2026-07-10T18:45:12.923Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><section>
  <p>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.</p>
</section>
<p>
  In the previous post,<sup>1</sup> 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.
</p>

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

<p>
The function (which I named <code>org-get-cust-priority</code>) 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
<code>org-get-property</code> function,<sup>2</sup> which I
named <code>org-get-std-priority</code>.  The two functions are shown below.<sup>3</sup>
</p>

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

(<span>defun</span> <span>org-get-std-priority</span> (s)
  <span>"Find priority cookie and return priority.</span>
<span>S is a string against which you can match `</span><span>org-priority-regexp</span><span>'.</span>
<span>Same function as `</span><span>org-get-priority</span><span>' sans test for a custom</span>
<span>function in `</span><span>org-priority-get-priority-function</span><span>'."</span>
  (<span>save-match-data</span>
    (<span>if</span> (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)))))))
</pre>
</div>


<p>
The number that Org uses to sort on priority depends on the values of
<code>org-priority-lowest</code>
and <code>org-priority-highest</code>,<sup>4</sup> 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.
</p>

<p>
You know you need this if you have about a dozen items on your agenda
and half of them are prioritized <code>A</code>.
</p>
<hr>
<p> 2026-08-13 Update org-get-cust-priority.  Previous version breaks org-tags-list.</p>
<p>
  <sup>1</sup> <a target="_blank" href="https://ray-on-emacs.blogspot.com/2026/07/numeric-priorities-in-org-mode.html">https://ray-on-emacs.blogspot.com/2026/07/numeric-priorities-in-org-mode.html</a><br>
  <sup>2</sup> Modified just to prevent recursion.<br>
  <sup>3</sup> 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.<br>
  <sup>4</sup> Or rather, the difference between the highest and lowest priorities.<br>
</p>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Numeric Priorities in Org Mode]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/07/numeric-priorities-in-org-mode.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/07/numeric-priorities-in-org-mode.html"/>
        <updated>2026-07-09T20:08:26.862Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
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.<sup>1</sup>
</p>

<p>
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.)
</p>

<p>
You can define priorities from 10 to 99, for example.  But if you try
to get to a priority higher than 10 using <code>org-priority-up</code>, 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.</p>

<div class="separator"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi110pVKheu5f3uP4uippZ77BBuQXEpGU-N1Johlxf0weLOjFwJeYbsrSRs2ufaz1luY0H0hAqZu8B1C6guTUQd7xbnUaS3LbVdejYwv8EAeKdPhqkLVF4mYzJSisyOAEXVeGITXuMBXtoRrVRNcs-wGsfEdkznob16-4o0rMOuWSFAf1X_8AyEh1OjMcI/s629/Clipbrd18.gif"><img alt="" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi110pVKheu5f3uP4uippZ77BBuQXEpGU-N1Johlxf0weLOjFwJeYbsrSRs2ufaz1luY0H0hAqZu8B1C6guTUQd7xbnUaS3LbVdejYwv8EAeKdPhqkLVF4mYzJSisyOAEXVeGITXuMBXtoRrVRNcs-wGsfEdkznob16-4o0rMOuWSFAf1X_8AyEh1OjMcI/s320/Clipbrd18.gif"></a></div>
<p>
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 <em>forty times</em>.
If you think you can invoke <code>M-4 0 M-x org-priority-up</code> to go quickly
from 50 to 10, you'll be disappointed (again).  The function
<code>org-priority-up</code> doesn't accept a prefix argument.<sup>2</sup>
</p>

<p>
How do you use priority in Org?
</p>
<hr>
<p>
  <sup>1</sup> <a target="_blank" href="https://orgmode.org/manual/Priorities.html#:~:text=You%20can%20change%20the%20range%20of%20allowed%20priorities">Org
  Manual -- Priorities</a>
</p>
<p><sup>2</sup> But you can record a macro that consists of a single
    S-&lt;up&gt; key press and then play it back with a prefix argument of 39.</p>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Configure Web Browser to be More Emacs-like]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/07/configure-web-browser-to-be-more-emacs.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/07/configure-web-browser-to-be-more-emacs.html"/>
        <updated>2026-07-08T20:35:47.102Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>
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 <a target="_blank" href="https://ray-on-emacs.blogspot.com/2026/06/add-web-search-to-libreoffice-writer.html">here</a>.  Another nice
feature is the ability to switch between the current buffer and the next (or
previous) buffer with <code>C-&lt;TAB&gt;</code> and <code>C-S-&lt;TAB&gt;</code>
that I added to Emacs<sup>1</sup>.
</p>

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

<p>
Why did I choose the key combination <code>C-S-k</code> instead
of <code>C-k</code>?  I think at the time <code>C-k</code> 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 <code>C-M-S-k</code> to "Close tabs to
the Left."
</p>

<p>
You might utter the following complaint, "But <code>C-S-k</code> 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 <code>C-S-k</code> to add it to the others.</p>

<p>I rely on the web browser's tab history a great deal, too.  I'm
  working on implementing something vaguely similar for Emacs."</p>
  
<hr>
<p><sup>1</sup> To get the <code>C-&lt;TAB&gt;</code>
      and <code>C-S-&lt;TAB&gt;</code> to switch buffers, just add
      this to your init file:<br>
</p><pre>  <code>(keymap-global-set "C-&lt;tab&gt;" 'next-buffer)</code>
  <code>(keymap-global-set "C-S-&lt;tab&gt;" 'previous-buffer)</code>
</pre><p></p>
<p><sup>2</sup> <a target="_blank" href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Killing-by-Lines.html">https://www.gnu.org/software/emacs/manual/html_node/emacs/Killing-by-Lines.html</a></p>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Emacs Carnival: diary, Part 2]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/06/emacs-carnival-diary-part-2.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/06/emacs-carnival-diary-part-2.html"/>
        <updated>2026-06-22T21:25:47.055Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>As I wrote earlier, I consider diary to be an underappreciated
  Emacs built-in.</p>
<p>I got interested in it again when I wanted to schedule a recurring
  meeting in Org for the third Tuesday of every month.  I saw on
  Reddit that some folks use <code>diary-float</code> in place of the usual active
  timestamp to achieve this.  So I added this after the meeting header
  to make it work:</p>

<pre>  * Third Tuesday of the Month Club
  SCHEDULED: &lt;(%%diary-float t 2 3) 19:00-20:00&gt;
</pre>

<p>I have five meetings that use this method of automatic scheduling.
  I like this approach because of its simplicity and elegance.
  Unfortunately, it doesn't behave the same way as a recurring task.</p>
<p>When I mark a recurring task as complete, Org reschedules it for the
  next date on which it should occur.  And it changes DONE to TODO to
  ensure the event shows up on the agenda for the next occurrence.</p>
<p>This doesn't happen for a task that's scheduled with <code>diary-float</code>.
  When marked complete, the headline remains at DONE; the next event
  won't show up on the agenda.</p>
  <p>And so Fengyuan Chen wrote <code>next-day-spec</code><sup>1</sup>
to solve this issue.  Unfortunately it doesn't work with the latest
versions of Emacs.</p>
<p>One of the many recognized cognitive biases is called the Sunk Cost
  Fallacy<sup>2</sup>, which impels an individual to favor an inferior (or
  incorrect) solution because a significant amount time, money, and/or
  effort has been invested in the solution.  Well, I've invested time
  and effort in both <code>diary-float</code>
  and <code>next-day-spec</code>, so I've continued to
  endorse it as a solution.</p>
<p>But even if rescheduling did work, I think it's better to have each
  meeting scheduled as a subheading of the overall meeting topic,
  like this:</p>

<pre>  * Third Tuesday of the Month Club
  ** DONE May 2026: &lt;2026-05-19 Tue&gt;
    :LOGBOOK:
    CLOCK: [2026-05-19 Tue 18:54]--[2026-05-19 Tue 20:12] =&gt;  1:18
    :END:
    - Note taken on [2026-05-19 Tue 21:20] \\
    We ate pizza.  Again.
    ** TODO June 2026: &lt;2026-06-23 Tue&gt;
      ** TODO July 2026: &lt;2026-07-21 Tue&gt;
</pre>

<p>The advantage to this is that clocked time and notes are organized
  neatly within each individual subtopic, not aggregated into one
  logbook and a series of notes.  If the aggregated time is desired,
  one could insert the log report.  I'll review this in another post.</p>
<p>But it's fun to schedule events that occur on irregular dates, such
  as National Engineers Week (US), which is the week in which George
  Washington's birth anniversary occurs<sup>3</sup>.  Here's how you
  can add it to an Org file:</p>

<pre>  * TODO Celebrate Engineers Week!
  SCHEDULED: &lt;%%(equal (calendar-gregorian-from-absolute (calendar-dayname-on-or-before 0 (calendar-absolute-from-gregorian (list 2 22 (calendar-extract-year date))))) date)&gt;
</pre>
<p>Do you have a favorite use for dairy?</p>

<sup>1</sup>
<a target="_blank" href="https://github.com/chenfengyuan/elisp/blob/master/next-spec-day.el">https://github.com/chenfengyuan/elisp/blob/master/next-spec-day.el</a><br>
<sup>2</sup> <a target="_blank" href="https://en.wikipedia.org/wiki/Sunk_cost#:~:text=sunk%20cost%20fallacy">Sunk cost - Wikipedia</a><br>
<sup>3</sup> <a target="_blank" href="https://www.holidayscalendar.com/event/national-engineers-week/">https://www.holidayscalendar.com/event/national-engineers-week/</a><br>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Emacs Carnival: diary, Part 1]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/06/emacs-carnival-diary-part-1.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/06/emacs-carnival-diary-part-1.html"/>
        <updated>2026-06-16T03:38:44.377Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>When I adopted Emacs in July of 2000, I hunkered down to learn the
  keybindings.  But after I learned the basics<sup>1</sup> I started
  to RTFM (<code>C-h r</code>), and I was instantly drawn to the
  Calendar/Diary<sup>2</sup> node, which I consider to be an
  underappreciated Emacs built-in, and, therefore, the topic of Emacs
  Carnival for June 2026.</p>
<p>Back then I mistook "diary" to mean a blank-page-confidant into
    which we write our thoughts, fears, ambitions or even just what we
    had for dinner last night.  At that time I'd been writing in
    a journal for about 24 years (yes, since 1976, on and off), so my head
    swelled with ideas of using Emacs for a revamped computerized
    version.  I imagined being able to <code>forward-search-regexp</code>
  in order
    to find, for example, that weird dream I wrote about in which I
    caught a toad that was hopping around the kitchen and then turned
    into a hot coal sizzling in my hand...</p>
<p>I used <code>diary-block</code> functions to date my entries and
  organize the content.  I ended up making four such entries from 2003
  to 2004.  But each day following the entry, <code>M-x diary</code>
  would display a nearly blank buffer, showing only the day's date at
  the top followed by a line of equal signs and perhaps a holiday.
  Where did yesterday's entry go?!  It was disconcerting that those
  words "vanished" into thin air magnetic film or
  silicon.</p>
<p>Today, however, I appreciate the simplicity of that approach -- a
    clean slate sans distractions.  But back then I was accustomed to
    seeing a vast amount of my writing, which fed my ego.</p>
<p>But that's not all there is to appreciate.  There are several
    functions that can be used for "an entry" in addition to
    <code>diary-block</code>, such as <code>diary-anniversary</code>, <code>diary-cyclic</code>, <code>diary-float</code> or even just a simple line that begins with a date and a brief note.  This is not an all-inclusive list.</p>
<p>Every fancy diary buffer can show local time of sunrise and sunset;
    to do this, include <code>diary-sunrise-sunset</code> in the diary file.
    Include <code>diary-lunar-phases</code> to show one of the four
    phases of the moon when one is active on that day; the
    local time of that moon phase will be included.</p>
<p>What follows is an abbreviated and edited listing of my diary
  files.  Note how I've structured <code>diary</code> into a hierarchy
  using <code>include</code> statements.</p>
<p><b>2026-06-17 02:25 GMT Important updates: First,</b> you'll need to modify two hook variables in order
  for the include statements to work.  Please see
  the <a target="_blank" href="https://www.gnu.org/software/emacs/manual/html_node/emacs/Fancy-Diary-Display.html#:~:text=Your%20main%20diary%20file%20can%20include%20other%20files">help
  for Fancy Diary Display</a>.  I also "use the normal hook
  <code>diary-list-entries-hook</code> to sort each day's diary
  entries by their time of day," which is described at the top of that
  page.  <b>Second,</b> specify the full path to the included files.
  I've modified the two include statements to add the "~/" path.  This
  is needed only if you want to press TAB on the item in the Agenda in
  order to focus the diary entry.</p>
<p> The beauty of this is that these events will show up in Org Agenda
    at the appropriate times when <code>org-agenda-include-diary</code>
  is non-nil.</p>
<p>Four examples are shown below.  Here are some things to note:
  </p><ul>
    <li>The content that's derived from the diary file has "Diary" for value of CATEGORY.</li>
    <li>The Sunrise / Sunset lines are supposed to show the time of
    each occurance.  However, the time for Sunrise isn't displayed;
    rather, it's indicated by its position on the time grid.  The times for both the New Moon and the Solar Eclipse are indicated by the time grid, as well, and are expected to peak at 13:38 in my area.  That's something to look faroward to!</li>
    <li>Sometimes I include links in my headings.  In this case I can
    follow a link to the credit card website to pay the Visa
    card.</li>
    <li>Even a link in the dairy file will be rendered
    in the Agenda properly, as shown in the reference for Richard
    Stallman's birthday.</li>
    </ul>

<blockquote>
  file listing: diary
  <pre>#    -*- mode: diary -*-<br>
#include "~/diary-anniversaries-property"
#include "~/diary-birthdays-friends"

%%(diary-sunrise-sunset)
%%(diary-lunar-phases)

%%(diary-remind '(diary-anniversary 3 16 1953) 14) [[https://html.duckduckgo.com/html/?q=Richard+Stallman+birthday][Richard Stallman's birthday]] in 14 days

%%(diary-block 6 21 2004 6 21 2004) The summer solstice.  I
always take time to observe the position of the sun on this
day, especially in the morning and evening.  Sunrise was at
5:16am and sunset will be at 8:29pm.  From today onward, the
days will be getting shorter.  At first it will be
imperceptible, but in September, it'll be quite noticeable.</pre>

file listing: diary-anniversaries-property
<pre>#    -*- mode: diary -*-
# Recurring Property events or records
%%(diary-remind '(diary-date 7 3 '(2023 2026 2029)) 30) Car Registration is due
%%(diary-anniversary 8 12 2022) Kitchen Cabinets Painted %d Years Ago</pre>

file listing: diary-birthdays-friends
<pre>#    -*- mode: diary -*-
# Birthdays of friends
%%(diary-anniversary 8 13) Bucky Thorndike Miller's Birthday (Does Dave still have his guitar amp?)
%%(diary-anniversary 2 13 1966) Ben's %d%s Birthday</pre>
</blockquote>

    
    <pre><span class="org-agenda-structure">Day-agenda (W11):</span>
<span class="org-agenda-date">Tuesday    16 March 2027</span>
<span class="org-agenda-diary">  Diary:       7:03 ┄┄┄┄┄ Sunrise (EDT), sunset 18:58 (EDT) at Home (11:54 hrs daylight)</span>
  <span class="org-time-grid">             8:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
<span class="org-imminent-deadline">  BILLS:       9:00 ┄┄┄┄┄ Deadline:   </span><span class="org-todo">TODO</span><span class="org-imminent-deadline"> Visa Card </span><span class="org-imminent-deadline"><span class="org-link"><a target="_blank" href="https://www.example.com/">LINK</a></span></span>            <span class="org-imminent-deadline"><span class="org-tag">:Bills::Credit:</span></span>
  <span class="org-time-grid">            10:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            12:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            14:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            16:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            18:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            20:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
<span class="org-agenda-diary">  Diary:      </span><span class="org-agenda-diary"><span class="org-link"><a target="_blank" href="https://html.duckduckgo.com/html/?q=Richard+Stallman+birthday">Richard Stallman's birthday</a></span></span><span class="org-agenda-diary"> in 14 days</span>
</pre>
<hr>
    <pre><span class="org-agenda-structure">Day-agenda (W23):</span>
<span class="org-agenda-date">Wednesday   3 June 2026</span>
<span class="org-agenda-diary">  Diary:       5:19 ┄┄┄┄┄ Sunrise (EDT), sunset 20:20 (EDT) at Home (15:00 hrs daylight)</span>
  <span class="org-time-grid">             8:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            10:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            12:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            14:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            16:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            18:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            20:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
<span class="org-agenda-diary">  Diary:      Reminder: Only 30 days until Car Registration is due</span>
</pre>
<hr>
    <pre><span class="org-agenda-structure">Day-agenda (W33):</span>
<span class="org-agenda-date">Wednesday  12 August 2026</span>
<span class="org-agenda-diary">  Diary:       5:58 ┄┄┄┄┄ Sunrise (EDT), sunset 19:54 (EDT) at Home (13:56 hrs daylight)</span>
  <span class="org-time-grid">             8:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">             9:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            10:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            12:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
<span class="org-agenda-diary">  Diary:      13:38 ┄┄┄┄┄ New Moon (EDT) ** Solar Eclipse **</span>
  <span class="org-time-grid">            14:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            16:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            18:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            20:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
<span class="org-agenda-diary">  Diary:      Kitchen Cabinets Painted 4 Years Ago</span>
</pre>
<hr>
    <pre><span class="org-agenda-structure">Day-agenda (W32):</span>
<span class="org-agenda-date-weekend">Saturday   13 August 2022</span>
<span class="org-agenda-diary">  Diary:       5:59 ┄┄┄┄┄ Sunrise (EDT), sunset 19:53 (EDT) at Home (13:53 hrs daylight)</span>
  <span class="org-time-grid">             8:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            10:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            12:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            14:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            16:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            18:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
  <span class="org-time-grid">            20:00 ┄┄┄┄┄ ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄</span>
<span class="org-agenda-diary">  Diary:      Bucky Thorndike Miller's Birthday (Does Dave still have his guitar amp?)</span>
</pre>
<hr>
    <sup>1</sup>My requirements (the basics) for a text editor were:
    <ul>
      <li>column editing</li>
      <li>keystroke recording and playback</li>
      <li>regular expression search and replace</li>
      <li>undo</li>
      <li>support multiple files in multiple windows</li>
    </ul>
    <sup>2</sup><a target="_blank" href="https://www.gnu.org/software/emacs/manual/html_node/emacs/index.html#SEC_Contents">Calendar/Diary in the GNU Emacs Manual</a>
<p></p></body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Creating a Reference to a Webpage in Org]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/06/creating-reference-to-webpage-in-org.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/06/creating-reference-to-webpage-in-org.html"/>
        <updated>2026-06-11T15:42:30.885Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>I was asked recently, "Do you use Org Mode protocol ... for browser
to Emacs interaction? If so, were there any complications to set it up
on Windows? Is there like bookmarklets for capturing or doing
  things?"<sup>1</sup></p>
<p>At the time I wasn't doing anything too fancy to incorporate Web
Content into Org.  I'd copy the URL from Vivaldi's address bar to the
clipboard and (if applicable) the Web Content I'm interested
  in.<sup>2</sup>  Then
in Org, I'd yank the Web Content (if applicable), mark it and then do
org-insert-link (C-c C-l) to turn it into a hyperlink.  The marked
text becomes what you see in the document.  But I usually just specify
the link Description simply as "LINK." In fact I created a macro
(bound to the Insert key) to do this.</p>
<p>But I've started to use eww as my web browser for this.  If I write
something that I need to verify, I'll mark it and invoke
eww-search-words (M-s M-w), which brings me to a DuckDuckGo page of
search results.  If I follow a search result that I like, I'll mark a
small section and invoke org-store-link (C-c l) and create the link
with org-insert-link, with the marked content as the Description.
Thus org-store-link captures both the location and the context in one
magical swoop.  Here's an animated GIF that illustrates the process.
Note that the video shows me capturing and referring to part of Sacha
Chua's website that shows some neat solutions to launching a web
browser from Emacs.<sup>3</sup></p>

<div class="separator"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvTadQW88KQZgsnlJsseOEC7dM_VioA4lvnNZ4b9AO6EYncYzKqRDqc-OM_79trnnq9gUYAu9_JGQOshax6MeZ_JDLJHKo00qBKIYjEV4uMV2q1fyFrKXT77yDQpP1uGJ0iVrPSKzX03sYzgOhL8QZWvrycBgpf3N-12hRwnK7qhtN4mu7SFpVFsncBDg/s1124/new_eww_to_org.gif"><img alt="" width="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvTadQW88KQZgsnlJsseOEC7dM_VioA4lvnNZ4b9AO6EYncYzKqRDqc-OM_79trnnq9gUYAu9_JGQOshax6MeZ_JDLJHKo00qBKIYjEV4uMV2q1fyFrKXT77yDQpP1uGJ0iVrPSKzX03sYzgOhL8QZWvrycBgpf3N-12hRwnK7qhtN4mu7SFpVFsncBDg/s400/new_eww_to_org.gif"></a></div>

<hr>
<sup>1</sup>
<span><a target="_blank" href="https://sachachua.com/blog/2026/05/emacs-chat-with-raymond-zeitler/#ID-ec23-transcript">Sacha
Chua's Emacs Chat with Raymond Zeitler transcript</a>.  Please scroll to
35:50</span><br>

<p><sup>2</sup> I use a clipboard manager so that I can copy content
to the clipboard multiple times without clobbering all but the most
recent item.</p>

<sup>3</sup>
<a target="_blank" href="https://sachachua.com/blog/2025/07/emacs-open-urls-or-search-the-web-plus-browse-url-handlers/">https://sachachua.com/blog/2025/07/emacs-open-urls-or-search-the-web-plus-browse-url-handlers/</a>

</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Raymond Zeitler: Emacs and the Numeric Keypad]]></title>
        <id>https://ray-on-emacs.blogspot.com/2026/06/emacs-and-numeric-keypad.html</id>
        <link href="https://ray-on-emacs.blogspot.com/2026/06/emacs-and-numeric-keypad.html"/>
        <updated>2026-06-08T15:51:22.851Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>If you have a numeric keypad<sup>1</sup>, try this in the *scratch* buffer:
enable numlock and press the zero on the numeric keyboard
(Num-0). Then press the zero located between the alpha keys and the
  function keys (0). Okay, you see 00, no big deal.</p>
<p>Why do I bring this up?  Emacs interprets Num-0 keypress as &lt;kp-0&gt; while it interprets 0 as
0.  This means that you could bind Num-0 to a function without
affecting the other 0. You could configure Num-0 to insert "zero" by
entering and evaluating this in the *scratch* buffer:</p>

<pre>(keymap-local-set <span>"&lt;kp-0&gt;"</span> #'(<span>lambda</span> () (<span>interactive</span>) (insert <span>"zero"</span>)))
</pre>

 <p>That's a trivial example, of course.  But it implies that you can
      have ten more keys to play with.  Similarly, the arithmetic
      operator keys on the numeric keyboard differ from the "regular
      ones" that are grouped with the alpha keys.  They can be bound
      to functions, referenced
      as &lt;kp-add&gt;, &lt;kp-subtract&gt;, &lt;kp-multiply&gt;, &lt;kp-divide&gt;.  But
      these also accept the standard C- M- S- modifier keys, which
      gives you twelve more possibilities.  This is true also for the
      Insert key &lt;kp-insert&gt; and the Delete key &lt;kp-delete&gt; that
      double as Num-0 and Num-., respectively.</p>

<p>I have a "Calc" key above the numeric keypad.  Unfortunately I
can't seem to use it for anything in Emacs -- it opens the OS's
calc.exe program regardless of what modifiers I use with it.  This is
a shame because it would be the ideal mapping for M-x calc.  Perhaps
this can be altered in BIOS.</p>

<p>But I do use my Win key as a modifier (sometimes). And I've heard
that the Caps Lock key can be repurposed.  These are topics for
  other posts.</p>

<hr>
<a target="_blank" href="https://en.wikipedia.org/wiki/Numeric_keypad">https://en.wikipedia.org/wiki/Numeric_keypad</a>
</body></html>]]></content>
        <author>
            <name>Raymond Zeitler</name>
            <uri>https://ray-on-emacs.blogspot.com/search/label/Emacs</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Alex Ott: One more time about Cedet]]></title>
        <id>http://alexott.blogspot.com/2008/12/one-more-time-about-cedet.html</id>
        <link href="http://alexott.blogspot.com/2008/12/one-more-time-about-cedet.html"/>
        <updated>2008-12-11T13:00:00.002Z</updated>
        <content type="html"><![CDATA[<html><head></head><body><p>In latest versions of  Cedet support of GNU Global was introduced, and very useful command - <span>semantic-symref</span>, was implemented.  It allows to find places in source code (for C &amp; C++ now) where given function is used. And if GTAGS database wasn't found, then this command tries to find occurrences with <span>find-grep</span> command.<br>As result, user gets something like this...</p><p></p><div class="separator"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6VXn4nr0f_yM3OcqdLBtE-l93RckBlGpMJcTMTvV8XLt-9r2Cc05FHO6G1P7u7JueEB31oqk2AEzxHLkVpq5Q3SZt8I2nLcdSC1GwQMD7iL0E7gSzRaBy1H8Bs6ZI09an39Ch-sJIWyhxff9HBycOG0lxylPoGwfMeszFm07fx1h2zWYIGjZYfg/s1032/cedet-symref.png"><img height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6VXn4nr0f_yM3OcqdLBtE-l93RckBlGpMJcTMTvV8XLt-9r2Cc05FHO6G1P7u7JueEB31oqk2AEzxHLkVpq5Q3SZt8I2nLcdSC1GwQMD7iL0E7gSzRaBy1H8Bs6ZI09an39Ch-sJIWyhxff9HBycOG0lxylPoGwfMeszFm07fx1h2zWYIGjZYfg/w640-h496/cedet-symref.png" width="640"></a></div><br><p></p></body></html>]]></content>
        <author>
            <name>Alex Ott</name>
            <uri>http://alexott.blogspot.com/search/label/emacs</uri>
        </author>
    </entry>
</feed>