<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Emacs Dwelling</title>
<link href="https://emacs.dyerdwelling.family/"/>
<link rel="self" href="https://emacs.dyerdwelling.family/index.xml"/>
<id>https://emacs.dyerdwelling.family/</id>
<updated>2026-04-30T11:00:00+0100</updated>
<author><name>James Dyer</name></author>
<entry>
<title>A Tiny Header line Tweak: Image Dimensions in image mode</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260430074958-emacs--a-tiny-header-line-tweak:-image-dimensions-in-image-mode/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260430074958-emacs--a-tiny-header-line-tweak:-image-dimensions-in-image-mode/</id>
<updated>2026-04-30T11:00:00+0100</updated>
<content type="html">&lt;p&gt;
I have been doing a lot of fiddling with images lately, mostly through dired and image-dired, and one little thing has been bugging me for a while.  When I open an image in Emacs, &lt;code&gt;image-mode&lt;/code&gt; happily shows me the picture, but it never tells me the one bit of information I actually want to know, how big is the thing?, width, height, file size, that sort of thing.  You can of course bounce out to a shell and run &lt;code&gt;identify&lt;/code&gt; or &lt;code&gt;file&lt;/code&gt;, but that feels silly when Emacs already has the image loaded.
&lt;/p&gt;


&lt;div id=&quot;orgde695df&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20260430074958-emacs--A-Tiny-Header-line-Tweak-Image-Dimensions-in-image-mode.jpg&quot; alt=&quot;20260430074958-emacs--A-Tiny-Header-line-Tweak-Image-Dimensions-in-image-mode.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
So I thought, right, this should be a five minute job, just slap something into the header-line on &lt;code&gt;image-mode-hook&lt;/code&gt; and be done with it.  And it more or less was, although there was a small wrinkle along the way that is worth mentioning, because it caught me out.
&lt;/p&gt;

&lt;p&gt;
Here is what ended up in my init.el:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre&gt;&lt;code class=&quot;language-lisp&quot;&gt;;;
;; -&amp;gt; image-mode-dimensions
;;

(defun my/image-mode-show-dimensions ()
  &quot;Display the open image&apos;s pixel dimensions and file size in the header line.&quot;
  (when (and (derived-mode-p &apos;image-mode)
             buffer-file-name
             (file-exists-p buffer-file-name))
    (condition-case err
        (let* ((image (or (image-get-display-property)
                          (create-image buffer-file-name)))
               (size (image-size image t))
               (width (car size))
               (height (cdr size))
               (bytes (file-attribute-size
                       (file-attributes buffer-file-name))))
          (setq header-line-format
                (format &quot; %d x %d px   %s&quot;
                        width height
                        (file-size-human-readable bytes))))
      (error
       (setq header-line-format
             (format &quot; image dimensions unavailable: %S&quot; err))))))

(add-hook &apos;image-mode-hook #&apos;my/image-mode-show-dimensions)
(add-hook &apos;image-mode-new-window-functions
          (lambda (&amp;amp;rest _) (my/image-mode-show-dimensions)))
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
A few notes on what is going on here.  The &lt;code&gt;or&lt;/code&gt; around &lt;code&gt;image-get-display-property&lt;/code&gt; and &lt;code&gt;create-image&lt;/code&gt; means we use the displayed image spec when it is there (cheaper, no extra file read), and fall back to building one from the file path when it is not.  &lt;code&gt;image-size&lt;/code&gt; with a non-nil second argument returns dimensions in actual pixels rather than canvas units, which is what I want.  &lt;code&gt;file-size-human-readable&lt;/code&gt; gives me a nice &lt;code&gt;2.4M&lt;/code&gt; rather than &lt;code&gt;2516582&lt;/code&gt;, because nobody reads bytes directly.
&lt;/p&gt;

&lt;p&gt;
The &lt;code&gt;condition-case&lt;/code&gt; is there because images can occasionally throw, especially when something has gone wrong with &lt;code&gt;imagemagick&lt;/code&gt; or an unsupported format sneaks in, and I would rather see a polite header-line message than have the hook explode and pollute &lt;code&gt;*Messages*&lt;/code&gt; every time I open a picture.
&lt;/p&gt;

&lt;p&gt;
The second hook, &lt;code&gt;image-mode-new-window-functions&lt;/code&gt;, is the one that handles the case where you flip between image and text view of the same buffer with &lt;code&gt;C-c C-c&lt;/code&gt;, since the image gets re-displayed and the header-line needs to refresh too.
&lt;/p&gt;

&lt;p&gt;
So now when I pop open an image, I get a nice little header-line that reads something like:
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;orgb194e9d&quot;&gt;
1920 x 1080 px   2.4M
&lt;/pre&gt;

&lt;p&gt;
A small thing, but the kind of small thing that makes Emacs feel like it fits a bit more snugly around how I actually work.
&lt;/p&gt;
</content>
</entry>
<entry>
<title>Highlighting git changes in a buffer with diff-hl</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260421070329-emacs--getting-diff-hl-just-right/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260421070329-emacs--getting-diff-hl-just-right/</id>
<updated>2026-04-21T08:00:00+0100</updated>
<content type="html">&lt;p&gt;
Lately I’ve found myself wanting a better, more fine-grained view of what’s going on in a file under &lt;code&gt;git&lt;/code&gt;. For some reason, my default workflow has been to keep jumping in and out of &lt;code&gt;project-vc-dir&lt;/code&gt; to check changes. It gets the job done, but honestly it’s a bit of a hassle
&lt;/p&gt;


&lt;div id=&quot;orge525edf&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20260421070329-emacs--Getting-diff-hl-Just-Right.jpg&quot; alt=&quot;20260421070329-emacs--Getting-diff-hl-Just-Right.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
What I really wanted was something right there in the buffer. Not a full-on inline diff (that gets messy fast I would guess), but just a small visual hint, something that lets me &quot;see&quot; what’s changed without breaking my flow.
&lt;/p&gt;

&lt;p&gt;
Turns out, that’s exactly what &lt;code&gt;diff-hl&lt;/code&gt; does
&lt;/p&gt;

&lt;p&gt;
It’s super lightweight and just highlights changes in the fringe. Nothing flashy but just enough to keep you aware of what you’ve modified. Once you start using it, it feels kind of weird not having it.
&lt;/p&gt;

&lt;p&gt;
One thing I really like is how nicely it plays with the built-in VC tools, move to a buffer position that aligns with a highlighted change, hit &lt;code&gt;C-x v =&lt;/code&gt; and it jumps straight to the relevant hunk in the diff. No friction, no extra thinking, it just works.
&lt;/p&gt;

&lt;p&gt;
Here’s the setup I’m using:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; diff-hl
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:ensure&lt;/span&gt; t
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:hook&lt;/span&gt; (dired-mode . diff-hl-dired-mode)
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:config&lt;/span&gt;
  (global-diff-hl-mode 1)
  (diff-hl-flydiff-mode 1)
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;unless&lt;/span&gt; (display-graphic-p)
    (diff-hl-margin-mode 1)))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
By default, &lt;code&gt;diff-hl-mode&lt;/code&gt; only updates when you save the file. That’s okay, but enabling &lt;code&gt;diff-hl-flydiff-mode&lt;/code&gt; makes it update as you type, which feels more intuitive.
&lt;/p&gt;

&lt;p&gt;
Oh, and that &lt;code&gt;dired-mode&lt;/code&gt; hook? That turns on &lt;code&gt;diff-hl-dired-mode&lt;/code&gt;, which gives you a quick visual overview of changed files right inside &lt;code&gt;dired&lt;/code&gt;. It’s one of those small touches that ends up being surprisingly useful.
&lt;/p&gt;

&lt;p&gt;
If you’ve got &lt;code&gt;repeat-mode&lt;/code&gt; enabled, you can also hop through changes with &lt;code&gt;C-x v ]&lt;/code&gt; and &lt;code&gt;C-x v [&lt;/code&gt;, which makes reviewing edits really smooth.
&lt;/p&gt;

&lt;p&gt;
I am enjoying &lt;code&gt;diff-hl&lt;/code&gt; and is quietly improving my workflow without getting in my way. Simple, fast, and just really nice to have.
&lt;/p&gt;
</content>
</entry>
<entry>
<title>Emacs-DIYer: A Built-in dired-collapse Replacement</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260409104443-emacs--emacs-diyer-a-built-in-dired-collapse-replacement/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260409104443-emacs--emacs-diyer-a-built-in-dired-collapse-replacement/</id>
<updated>2026-04-15T19:20:00+0100</updated>
<content type="html">&lt;p&gt;
I have been slowly chipping away at my &lt;a href=&quot;https://github.com/captainflasmr/Emacs-DIYer&quot;&gt;Emacs-DIYer&lt;/a&gt; project, which is basically my ongoing experiment in rebuilding popular Emacs packages using only what ships with Emacs itself, no external dependencies, no MELPA, just the built-in pieces bolted together in a literate &lt;code&gt;README.org&lt;/code&gt; that tangles to &lt;code&gt;init.el&lt;/code&gt;.  The latest addition is a DIY version of &lt;code&gt;dired-collapse&lt;/code&gt; from the &lt;code&gt;dired-hacks&lt;/code&gt; family, which is one of those packages I did not realise I leaned on until I started browsing a deeply-nested Java project and felt the absence immediately.
&lt;/p&gt;


&lt;div id=&quot;org4786bdb&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20260409104443-emacs--Emacs-DIYer-A-Built-in-dired-collapse-Replacement.jpg&quot; alt=&quot;20260409104443-emacs--Emacs-DIYer-A-Built-in-dired-collapse-Replacement.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
If you have ever opened a dired buffer on something like a Maven project, or &lt;code&gt;node_modules&lt;/code&gt;, or a freshly generated resource bundle, you will know the pain, &lt;code&gt;src/&lt;/code&gt; contains a single &lt;code&gt;main/&lt;/code&gt; which contains a single &lt;code&gt;java/&lt;/code&gt; which contains a single &lt;code&gt;com/&lt;/code&gt; which contains a single &lt;code&gt;example/&lt;/code&gt;, and you are pressing &lt;code&gt;RET&lt;/code&gt; four times just to get to anything interesting.  The &lt;code&gt;dired-collapse&lt;/code&gt; minor mode from &lt;code&gt;dired-hacks&lt;/code&gt; solves this beautifully, it squashes that whole single-child chain into one dired line so &lt;code&gt;src/main/java/com/example/&lt;/code&gt; shows up as a single row and one &lt;code&gt;RET&lt;/code&gt; drops you straight into the deepest directory.
&lt;/p&gt;

&lt;p&gt;
So, as always with the Emacs-DIYer project, I wondered, can I implement this in a few elisp defuns?
&lt;/p&gt;

&lt;p&gt;
Right, so what is the plan?, dired already draws a nice listing with permissions, sizes, dates and filenames, all I really need to do is walk each line, look at the directory, figure out the deepest single-child descendant, and then rewrite the filename column in place with the collapsed path.  The trick, and this is the bit that took me a minute to convince myself of, is that dired uses a &lt;code&gt;dired-filename&lt;/code&gt; text property to know where the filename lives on the line, and &lt;code&gt;dired-get-filename&lt;/code&gt; happily accepts relative paths containing slashes.  So if I can rewrite the text and reapply the property, everything else, &lt;code&gt;RET&lt;/code&gt;, marking, copying, should just work without me having to touch the rest of dired at all!
&lt;/p&gt;

&lt;p&gt;
First function, &lt;code&gt;my/dired-collapse--deepest&lt;/code&gt;, which just walks the directory chain as long as each directory contains exactly one accessible child directory.  I added a 100-iteration guard so a pathological symlink cycle cannot wedge the whole thing, which, you know, future me might thank present me for:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;my/dired-collapse--deepest&lt;/span&gt; (dir)
  &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Return the deepest single-child descendant directory of DIR.
Walks the directory chain as long as each directory contains exactly
one entry which is itself an accessible directory.  Stops after 100
iterations to guard against symlink cycles.&quot;&lt;/span&gt;
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((current dir)
        (depth 0))
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;catch&lt;/span&gt; &apos;&lt;span style=&quot;color: #ee7b29;&quot;&gt;done&lt;/span&gt;
      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;while&lt;/span&gt; (&amp;lt; depth 100)
        (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((entries (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;condition-case&lt;/span&gt; nil
                           (directory-files current t
                                            directory-files-no-dot-files-regexp
                                            t)
                         (&lt;span style=&quot;color: #930000; font-weight: bold;&quot;&gt;error&lt;/span&gt; nil))))
          (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; entries
                   (null (cdr entries))
                   (file-directory-p (car entries))
                   (file-accessible-directory-p (car entries)))
              (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; current (car entries)
                    depth (1+ depth))
            (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;throw&lt;/span&gt; &apos;&lt;span style=&quot;color: #ee7b29;&quot;&gt;done&lt;/span&gt; current)))))
    current))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
&lt;code&gt;directory-files-no-dot-files-regexp&lt;/code&gt; is one of those lovely little built-in constants I keep forgetting exists, it filters out &lt;code&gt;.&lt;/code&gt; and &lt;code&gt;..&lt;/code&gt; but keeps dotfiles, which is exactly what you want if you are deciding whether a directory is truly single-child.
&lt;/p&gt;

&lt;p&gt;
Second function does the actual buffer surgery, &lt;code&gt;my/dired-collapse&lt;/code&gt; iterates each dired line, grabs the filename with &lt;code&gt;dired-get-filename&lt;/code&gt;, asks the walker how deep the chain goes, and if there is anything to collapse it replaces the displayed filename with the collapsed relative path:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;my/dired-collapse&lt;/span&gt; ()
  &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Collapse single-child directory chains in the current dired buffer.
A DIY replacement for `&lt;/span&gt;&lt;span style=&quot;color: #ee7b29;&quot;&gt;dired-collapse-mode&lt;/span&gt;&lt;span style=&quot;color: #7bc5e1;&quot;&gt;&apos; from the dired-hacks
package.  Rewrites the filename portion of each line in place and
reapplies the `&lt;/span&gt;&lt;span style=&quot;color: #ee7b29;&quot;&gt;dired-filename&lt;/span&gt;&lt;span style=&quot;color: #7bc5e1;&quot;&gt;&apos; text property so that standard dired
navigation still resolves to the deepest directory.&quot;&lt;/span&gt;
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; (derived-mode-p &apos;dired-mode)
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((inhibit-read-only t))
      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;save-excursion&lt;/span&gt;
        (goto-char (point-min))
        (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;while&lt;/span&gt; (not (eobp))
          (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;condition-case&lt;/span&gt; nil
              (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((file (dired-get-filename nil t)))
                (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; file
                           (file-directory-p file)
                           (not (member (file-name-nondirectory
                                         (directory-file-name file))
                                        &apos;(&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;.&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;..&quot;&lt;/span&gt;)))
                           (file-accessible-directory-p file))
                  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((deepest (my/dired-collapse--deepest file)))
                    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;unless&lt;/span&gt; (string= deepest file)
                      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; (dired-move-to-filename)
                        (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let*&lt;/span&gt; ((start (point))
                               (end (dired-move-to-end-of-filename t))
                               (displayed (buffer-substring-no-properties
                                           start end))
                               (suffix (substring deepest
                                                  (1+ (length file))))
                               (new (concat displayed &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/&quot;&lt;/span&gt; suffix)))
                          (delete-region start end)
                          (goto-char start)
                          (insert (propertize new
                                              &apos;face &apos;dired-directory
                                              &apos;mouse-face &apos;highlight
                                              &apos;dired-filename t))))))))
            (&lt;span style=&quot;color: #930000; font-weight: bold;&quot;&gt;error&lt;/span&gt; nil))
          (forward-line))))))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
The key bit is the &lt;code&gt;propertize&lt;/code&gt; call at the end, the new filename text has to carry &lt;code&gt;dired-filename t&lt;/code&gt; so that &lt;code&gt;dired-get-filename&lt;/code&gt; picks it up, and &lt;code&gt;dired-directory&lt;/code&gt; on &lt;code&gt;face&lt;/code&gt; keeps the collapsed entry looking the same as a normal directory line.  Because &lt;code&gt;dired-get-filename&lt;/code&gt; will happily glue a relative path like &lt;code&gt;main/java/com/example&lt;/code&gt; onto the dired buffer&apos;s directory, pressing &lt;code&gt;RET&lt;/code&gt; on a collapsed line takes you straight to &lt;code&gt;src/main/java/com/example&lt;/code&gt; with no extra work from me.
&lt;/p&gt;

&lt;p&gt;
A while back I added a little unicode icon overlay thing to dired (&lt;code&gt;my/dired-add-icons&lt;/code&gt;, which puts a little symbol in front of each filename via a zero-length overlay), and I did not want the collapse to fight with it.  The icons hook into &lt;code&gt;dired-after-readin-hook&lt;/code&gt; as well, so I just gave collapse a negative depth when attaching its hook:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(add-hook &apos;dired-after-readin-hook #&apos;my/dired-collapse -50)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Lower depth runs earlier, so collapse rewrites the line first, then the icon overlay attaches to the final collapsed filename position.  Without this, the icons would happily sit in front of a stub directory that was about to be rewritten, which is, well, fine I suppose, but it felt tidier to have them anchor on the post-collapse text.
&lt;/p&gt;

&lt;p&gt;
Before, a typical Maven project root might look something like this:
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;orga995156&quot;&gt;
drwxr-xr-x 3 jdyer users 4096 Apr  9 08:12 ▶ src
drwxr-xr-x 2 jdyer users 4096 Apr  9 08:11 ▶ target
-rw-r--r-- 1 jdyer users  812 Apr  9 08:10 ◦ pom.xml
&lt;/pre&gt;

&lt;p&gt;
After collapse kicks in:
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;org1e9170c&quot;&gt;
drwxr-xr-x 3 jdyer users 4096 Apr  9 08:12 ▶ src/main/java/com/example
drwxr-xr-x 2 jdyer users 4096 Apr  9 08:11 ▶ target
-rw-r--r-- 1 jdyer users  812 Apr  9 08:10 ◦ pom.xml
&lt;/pre&gt;

&lt;p&gt;
One &lt;code&gt;RET&lt;/code&gt; and you are in &lt;code&gt;com/example&lt;/code&gt;, which is where all the actual code lives anyway.  Marking, copying, deleting, renaming, all of it still behaves because the &lt;code&gt;dired-filename&lt;/code&gt; text property points at the real deepest path.
&lt;/p&gt;

&lt;p&gt;
One thing that initially bit me, is navigating &lt;b&gt;out&lt;/b&gt; of a collapsed chain.  If I hit &lt;code&gt;RET&lt;/code&gt; on a collapsed &lt;code&gt;src/main/java/com/example&lt;/code&gt; line I land in the deepest directory, which is great, but then pressing my usual &lt;code&gt;M-e&lt;/code&gt; to go back up was doing the wrong thing.  &lt;code&gt;M-e&lt;/code&gt; in my config has always been bound to &lt;code&gt;dired-jump&lt;/code&gt;, and &lt;code&gt;dired-jump&lt;/code&gt; called from inside a dired buffer does a &quot;pop up a level&quot; thing that ended up spawning a fresh dired for &lt;code&gt;com/&lt;/code&gt;, bypassing the collapsed view entirely and leaving me staring at a directory I never wanted to see.
&lt;/p&gt;

&lt;p&gt;
My first attempt at fixing this was to put some around-advice on &lt;code&gt;dired-jump&lt;/code&gt; so that if an existing dired buffer already had a collapsed line covering the jump target, it would switch to that buffer and land on the collapsed line instead of splicing in a duplicate subdir.  It worked, sort of, but &lt;code&gt;dired-jump&lt;/code&gt; in general felt a bit janky inside dired, it does a lot of &quot;refresh the buffer and try again&quot; under the hood and the in-dired pop-up-a-level path was always the weak link.  So I stepped back and split the two cases apart with a tiny dispatch wrapper:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;my/dired-jump-or-up&lt;/span&gt; ()
  &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;If in Dired, go up a directory; otherwise dired-jump for current buffer.&quot;&lt;/span&gt;
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;interactive&lt;/span&gt;)
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; (derived-mode-p &apos;dired-mode)
      (dired-up-directory)
    (dired-jump)))&lt;br&gt;
(global-set-key (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;M-e&quot;&lt;/span&gt;) #&apos;my/dired-jump-or-up)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
From a file buffer, &lt;code&gt;dired-jump&lt;/code&gt; is still exactly the right thing as you want the directory the file is in of course.  From inside a dired buffer, &lt;code&gt;dired-up-directory&lt;/code&gt; is just a much cleaner operation, it walks up one real level, no refresh, no splicing, nothing weird.  But on its own that would lose the collapsed round-trip, so I gave &lt;code&gt;dired-up-directory&lt;/code&gt; its own bit of advice that looks for a collapsed-ancestor buffer before falling through to the default behaviour.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;my/dired-collapse--find-hit&lt;/span&gt; (target-dir)
  &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Return (BUFFER . POS) of a dired buffer with a collapsed line covering TARGET-DIR.&quot;&lt;/span&gt;
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((target (file-name-as-directory (expand-file-name target-dir)))
        hit)
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;dolist&lt;/span&gt; (buf (buffer-list))
      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;unless&lt;/span&gt; hit
        (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-current-buffer&lt;/span&gt; buf
          (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; (derived-mode-p &apos;dired-mode)
                     (stringp default-directory))
            (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((buf-dir (file-name-as-directory
                            (expand-file-name default-directory))))
              (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; (string-prefix-p buf-dir target)
                         (not (string= buf-dir target)))
                (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;save-excursion&lt;/span&gt;
                  (goto-char (point-min))
                  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;catch&lt;/span&gt; &apos;&lt;span style=&quot;color: #ee7b29;&quot;&gt;found&lt;/span&gt;
                    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;while&lt;/span&gt; (not (eobp))
                      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((line-file (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;ignore-errors&lt;/span&gt;
                                         (dired-get-filename nil t))))
                        (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; line-file
                                   (file-directory-p line-file))
                          (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((line-dir (file-name-as-directory
                                           (expand-file-name line-file))))
                            (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; (string-prefix-p target line-dir)
                              (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; hit (cons buf (point)))
                              (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;throw&lt;/span&gt; &apos;&lt;span style=&quot;color: #ee7b29;&quot;&gt;found&lt;/span&gt; nil)))))
                      (forward-line))))))))))
    hit))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
The &lt;code&gt;dired-up-directory&lt;/code&gt; only fires when the literal parent is &lt;b&gt;not&lt;/b&gt; already open as a dired buffer, which keeps normal upward navigation completely unchanged:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;my/dired-collapse--up-advice&lt;/span&gt; (orig-fn &lt;span style=&quot;color: #ee7b29;&quot;&gt;&amp;amp;optional&lt;/span&gt; other-window)
  &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Around-advice for `&lt;/span&gt;&lt;span style=&quot;color: #ee7b29;&quot;&gt;dired-up-directory&lt;/span&gt;&lt;span style=&quot;color: #7bc5e1;&quot;&gt;&apos; restoring collapsed round-trip.&quot;&lt;/span&gt;
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let*&lt;/span&gt; ((dir (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; (derived-mode-p &apos;dired-mode)
                   (stringp default-directory)
                   (expand-file-name default-directory)))
         (up (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; dir (file-name-directory (directory-file-name dir))))
         (parent-buf (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; up (dired-find-buffer-nocreate up)))
         (hit (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; dir (null parent-buf)
                   (my/dired-collapse--find-hit dir))))
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; hit
        (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((buf (car hit))
              (pos (cdr hit)))
          (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; other-window
              (switch-to-buffer-other-window buf)
            (pop-to-buffer-same-window buf))
          (goto-char pos)
          (dired-move-to-filename))
      (funcall orig-fn other-window))))&lt;br&gt;
(advice-add &apos;dired-up-directory &lt;span style=&quot;color: #fedd38;&quot;&gt;:around&lt;/span&gt; #&apos;my/dired-collapse--up-advice)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
If &lt;code&gt;/proj/src/main/java/com/&lt;/code&gt; happens to already exist as a dired buffer, &lt;code&gt;dired-up-directory&lt;/code&gt; does its usual thing and just goes there, the up-advice never fires.  It is only when the literal parent is absent that the advice kicks in and hands you back to the collapsed ancestor, which I think is the right tradeoff, the advice never surprises you when you were going to get the standard behaviour anyway, it only steps in when the standard behaviour would throw away context you clearly still had in a buffer somewhere.
&lt;/p&gt;

&lt;p&gt;
End result, &lt;code&gt;RET&lt;/code&gt; into a collapsed chain drops me deep, &lt;code&gt;M-e&lt;/code&gt; walks me back out to the original collapsed line, and none of it requires doing anything clever with &lt;code&gt;dired-jump&lt;/code&gt;&apos;s &quot;pop up a level&quot; path, which I am increasingly convinced I should not have been using in the first place.
&lt;/p&gt;

&lt;p&gt;
Everything lives in the &lt;a href=&quot;https://github.com/captainflasmr/Emacs-DIYer&quot;&gt;Emacs-DIYer&lt;/a&gt; project on GitHub, in the literate &lt;code&gt;README.org&lt;/code&gt;.  If you just want the snippet to drop into your own init file, the two functions and the &lt;code&gt;add-hook&lt;/code&gt; line above are the whole thing, no &lt;code&gt;require&lt;/code&gt;, no &lt;code&gt;use-package&lt;/code&gt;, no MELPA, just built-in dired and a bit of buffer shenanigans, and thats it!, phew, and breathe!
&lt;/p&gt;
</content>
</entry>
<entry>
<title>Wiring Flymake Diagnostics into a Follow Mode</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260409061315-emacs--wiring-flymake-diagnostics-into-a-follow-mode/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260409061315-emacs--wiring-flymake-diagnostics-into-a-follow-mode/</id>
<updated>2026-04-09T06:13:00+0100</updated>
<content type="html">&lt;p&gt;
Flymake has been quietly sitting in my config for years doing exactly what it says on the tin, squiggly lines under things that are wrong, and I mostly left it alone. But recently I noticed I was doing the same little dance over and over: spot a warning, squint at the modeline counter, run `M-x flymake-show-buffer-diagnostics`, scroll through the list to find the thing I was actually looking at, then flip back. Two windows, zero connection between them.
&lt;/p&gt;

&lt;p&gt;
So I wired it up properly, and while I was in there I gave it a set of keybindings that feel right to my muscle memory.
&lt;/p&gt;


&lt;div id=&quot;orgcd72df3&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20260409061315-emacs--Wiring-Flymake-Diagnostics-into-a-Follow-Mode.jpg&quot; alt=&quot;20260409061315-emacs--Wiring-Flymake-Diagnostics-into-a-Follow-Mode.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
The obvious bindings for stepping through errors are `M-n` and `M-p`, and most people using flymake bind exactly those. The problem is that in my config `M-n` and `M-p` are already taken, they step through simply-annotate annotations (which is itself a very handy thing and I am not giving it up!). So I shifted a key up and went with the shifted variants: `M-N` for next, `M-P` for previous, and `M-M` to toggle the diagnostics buffer.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; flymake-show-diagnostics-at-end-of-line nil)
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-eval-after-load&lt;/span&gt; &apos;flymake
    (define-key flymake-mode-map (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;M-N&quot;&lt;/span&gt;) #&apos;flymake-goto-next-error)
    (define-key flymake-mode-map (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;M-P&quot;&lt;/span&gt;) #&apos;flymake-goto-prev-error))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
With &lt;code&gt;M-M&lt;/code&gt; I wanted it to be a bit smarter than just &quot;open the buffer&quot;. If it is already visible I want it gone, if it is not I want it up. The standard toggle pattern:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;my/flymake--diag-buffer&lt;/span&gt; ()
    &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Return the visible flymake diagnostics buffer, or nil.&quot;&lt;/span&gt;
    (seq-some (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;lambda&lt;/span&gt; (b)
                (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-current-buffer&lt;/span&gt; b
                       (derived-mode-p &apos;flymake-diagnostics-buffer-mode))
                     (get-buffer-window b)
                     b))
              (buffer-list)))&lt;br&gt;
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;my/flymake-toggle-diagnostics&lt;/span&gt; ()
    &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Toggle the flymake diagnostics buffer.&quot;&lt;/span&gt;
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;interactive&lt;/span&gt;)
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((buf (my/flymake--diag-buffer)))
      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; buf
          (quit-window nil (get-buffer-window buf))
        (flymake-show-buffer-diagnostics)
        (my/flymake-sync-diagnostics))))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Now the interesting bit. What I really wanted was a follow mode, something like how the compilation buffer tracks position or how Occur highlights the current hit. When my point lands on an error in the source buffer, the corresponding row in the diagnostics buffer should light up. That way the diagnostics window becomes a live index of where I am rather than a static dump and think in general this is how a lot of other IDEs work.
&lt;/p&gt;

&lt;p&gt;
I tried the lazy route first, turning on hl-line-mode in the diagnostics buffer and calling hl-line-highlight from a post-command-hook in the source buffer. The line lit up once and then refused to move. Nothing I did would shift it. This is because hl-line-highlight is really only designed to be driven from the window whose line is being highlighted, and I was firing it from afar.
&lt;/p&gt;

&lt;p&gt;
Ok, so why not just manage my own overlay:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defvar&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;my/flymake--sync-overlay&lt;/span&gt; nil
    &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Overlay used to highlight the current entry in the diagnostics buffer.&quot;&lt;/span&gt;)&lt;br&gt;
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;my/flymake-sync-diagnostics&lt;/span&gt; ()
    &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Highlight the diagnostics buffer entry matching the error at point.&quot;&lt;/span&gt;
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when-let*&lt;/span&gt; ((buf (my/flymake--diag-buffer))
                (win (get-buffer-window buf))
                (diag (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;or&lt;/span&gt; (car (flymake-diagnostics (point)))
                          (car (flymake-diagnostics (line-beginning-position)
                                                    (line-end-position))))))
      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-current-buffer&lt;/span&gt; buf
        (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;save-excursion&lt;/span&gt;
          (goto-char (point-min))
          (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((found nil))
            (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;while&lt;/span&gt; (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; (not found) (not (eobp)))
              (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((id (tabulated-list-get-id)))
                (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;and&lt;/span&gt; (listp id) (eq (plist-get id &lt;span style=&quot;color: #fedd38;&quot;&gt;:diagnostic&lt;/span&gt;) diag))
                    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; found (point))
                  (forward-line 1))))
            (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; found
              (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;unless&lt;/span&gt; (overlayp my/flymake--sync-overlay)
                (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; my/flymake--sync-overlay (make-overlay 1 1))
                (overlay-put my/flymake--sync-overlay &apos;face &apos;highlight)
                (overlay-put my/flymake--sync-overlay &apos;priority 100))
              (move-overlay my/flymake--sync-overlay
                            found
                            (min (point-max) (1+ (line-end-position)))
                            buf)
              (set-window-point win found)))))))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
My first pass at the walk through the tabulated list did not work. I was comparing (tabulated-list-get-id) directly against the diagnostic returned by flymake-diagnostics using eq, and it was always false, which meant found stayed nil forever and the overlay never moved. A dive into flymake.el revealed why. Each row in the diagnostics buffer stores its ID as a plist, not as the diagnostic itself:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;  (list :diagnostic diag
        :line line
        :severity ...)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
So I need to pluck out :diagnostic before comparing. Obvious in hindsight, as these things always are. With plist-get in place the comparison lines up and the overlay moves exactly where I want it, tracking every navigation command.
&lt;/p&gt;

&lt;p&gt;
The fallback lookup using line-beginning-position and line-end-position is there because flymake-diagnostics (point) only returns something if point is strictly inside the diagnostic span. When I land between errors or on the same line as an error but a few columns off, I still want the diagnostics buffer to track, so I widen the search to the whole line.
&lt;/p&gt;

&lt;p&gt;
Finally, wrap the hook in a minor mode so I can toggle it per buffer and enable it automatically whenever flymake comes up:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;define-minor-mode&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;my/flymake-follow-mode&lt;/span&gt;
    &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Sync the diagnostics buffer to the error at point.&quot;&lt;/span&gt;
    &lt;span style=&quot;color: #fedd38;&quot;&gt;:lighter&lt;/span&gt; nil
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; my/flymake-follow-mode
        (add-hook &apos;post-command-hook #&apos;my/flymake-sync-diagnostics nil t)
      (remove-hook &apos;post-command-hook #&apos;my/flymake-sync-diagnostics t)))&lt;br&gt;
  (add-hook &apos;flymake-mode-hook #&apos;my/flymake-follow-mode)
  (define-key flymake-mode-map (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;M-M&quot;&lt;/span&gt;) #&apos;my/flymake-toggle-diagnostics)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
The end result is nice. M-M pops the diagnostics buffer, M-N and M-P walk through the errors, and as I navigate the source the matching row in the diagnostics buffer highlights in step with me. If I close the buffer with another M-M everything goes quiet, and I can still step through with M-N/M-P on their own.
&lt;/p&gt;

&lt;p&gt;
Three little keybindings and twenty lines of elisp, but they turn flymake from a static reporter into something that actually feels connected to where I am in the buffer.
&lt;/p&gt;
</content>
</entry>
<entry>
<title>Simply Annotate 0.9.8: Threaded Conversations on Your Code</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260329084330-emacs--simply-annotate-0.9.8:-threaded-conversations-on-your-code/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260329084330-emacs--simply-annotate-0.9.8:-threaded-conversations-on-your-code/</id>
<updated>2026-03-29T09:08:00+0100</updated>
<content type="html">&lt;p&gt;
I have been busy improving my annotation package! &lt;a href=&quot;https://github.com/captainflasmr/simply-annotate&quot;&gt;Simply Annotate&lt;/a&gt;, the latest release is 0.9.8 and I have put in a bunch of new features, so it felt like a good time to step back and show what the package actually does at this point, because honestly, quite a lot has changed since I last wrote about it.
&lt;/p&gt;


&lt;div id=&quot;org6b944e8&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/simply-annotate-banner.jpg&quot; alt=&quot;simply-annotate-banner.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
There are annotation packages out there already, &lt;code&gt;annotate.el&lt;/code&gt; being the most established.  And they are good!  But I kept running into the same friction: I wanted threaded conversations directly on my code, I wanted multiple display styles I could combine, and I wanted the whole thing to be a single file with no dependencies that I could drop onto an air-gapped machine and just use (yup, that again!)
&lt;/p&gt;

&lt;p&gt;
So I built my own!, this is Emacs, after all.
&lt;/p&gt;


&lt;div id=&quot;orgda8a728&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/simply-annotate-screen-recording.gif&quot; alt=&quot;simply-annotate-screen-recording.gif&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
At its core, simply-annotate lets you attach persistent notes to any text file (or Info manual, or dired buffer) without modifying the original content.  Annotations are stored in a simple s-expression database at &lt;code&gt;~/.emacs.d/simply-annotations.el&lt;/code&gt;.  The entire package is a single elisp file, requires Emacs 28.1+, and has zero external dependencies.
&lt;/p&gt;

&lt;p&gt;
Basic setup is two lines:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; simply-annotate
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:bind-keymap&lt;/span&gt; (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c a&quot;&lt;/span&gt; . simply-annotate-command-map)
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:hook&lt;/span&gt; (find-file-hook . simply-annotate-mode))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Or if you prefer &lt;code&gt;require&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;require&lt;/span&gt; &apos;&lt;span style=&quot;color: #ee7b29;&quot;&gt;simply-annotate&lt;/span&gt;)
(global-set-key (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c a&quot;&lt;/span&gt;) simply-annotate-command-map)
(add-hook &apos;find-file-hook #&apos;simply-annotate-mode)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Open a file, select some text, press &lt;code&gt;C-c a j&lt;/code&gt;, and you have your first annotation.  &lt;code&gt;M-n&lt;/code&gt; and &lt;code&gt;M-p&lt;/code&gt; step through them.
&lt;/p&gt;

&lt;p&gt;
I am always fiddling around with styles, themes, backgrounds e.t.c, so I thought I would build this tinkering enthusiasm into this package.  Simply-annotate has five display styles, and you can layer them together:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;Highlight&lt;/b&gt; &amp;#x2013; classic background colour on the annotated region&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Tint&lt;/b&gt; &amp;#x2013; a subtle background derived from your current theme, lightened by a configurable amount.  Adapts automatically when you switch themes&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fringe&lt;/b&gt; &amp;#x2013; a small triangle indicator in the fringe, minimal and unobtrusive&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fringe-bracket&lt;/b&gt; &amp;#x2013; a vertical bracket spanning the full annotated region in the fringe, with a proper top cap, continuous vertical bar, and bottom cap&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Subtle&lt;/b&gt; &amp;#x2013; overline and underline bracketing the region, barely visible but there when you need it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
You can combine them, so &lt;code&gt;(tint fringe-bracket)&lt;/code&gt; gives you a gentle background wash with a clear fringe bracket showing exactly where the annotation spans.  Cycle through styles with &lt;code&gt;C-c a &apos;&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Toggle inline display with &lt;code&gt;C-c a /&lt;/code&gt; and annotation content appears as box-drawn blocks directly in your buffer:
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;orgc7db0b0&quot;&gt;
    some annotated code here
    ▴
┌─ ✎ [OPEN/NORMAL] ──────────────
│ This function needs refactoring.
│ The nested conditionals are hard
│ to follow.
└─────────────────────────────────
&lt;/pre&gt;

&lt;p&gt;
New in 0.9.8 is the inline pointer, that little &lt;code&gt;▴&lt;/code&gt; connecting the box to the annotated text.  It is indented to the exact column where the annotation starts, so you always know what the comment refers to.
&lt;/p&gt;

&lt;p&gt;
The fun bit is that the pointer is just a string, and it supports multiline.  So you can customise it to whatever shape you like:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Simple arrow (default)
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-after &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9652;&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-above &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9662;&quot;&lt;/span&gt;)&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Heavy L-bracket (my current favourite)
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-after &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9475;\n&amp;#9495;&amp;#9473;&amp;#9654;&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-above &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9487;&amp;#9473;&amp;#9654;\n&amp;#9475;&quot;&lt;/span&gt;)&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Speech bubble tail
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-after &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot; &amp;#9584;&amp;#9488;&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-above &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot; &amp;#9584;&amp;#9488;&quot;&lt;/span&gt;)&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Decorative diamond
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-after &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9670;&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-above &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9670;&quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
There is a full list of copy-paste options in the Commentary section of the elisp file.  Set to &lt;code&gt;nil&lt;/code&gt; to disable the pointer entirely.
&lt;/p&gt;

&lt;p&gt;
So I think this is where simply-annotate really differs from other annotation packages.  Every annotation is a thread.  You can reply to it with &lt;code&gt;C-c a r&lt;/code&gt;, and replies can be nested under any comment in the thread, not just the root.  It prompts with a hierarchical completing-read menu showing the comment tree.
&lt;/p&gt;

&lt;p&gt;
Each thread has:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;Status&lt;/b&gt; &amp;#x2013; open, in-progress, resolved, closed (&lt;code&gt;C-c a s&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Priority&lt;/b&gt; &amp;#x2013; low, normal, high, critical (&lt;code&gt;C-c a p&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Tags&lt;/b&gt; &amp;#x2013; freeform hashtags for organisation (&lt;code&gt;C-c a t&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Author tracking&lt;/b&gt; &amp;#x2013; configurable per-team, per-file, or single-user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The comment tree renders with box-drawing characters so the hierarchy is always clear:
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;org14d431c&quot;&gt;
┌— ® [OPEN/NORMAL] —
| james dyer (03/29 08:27)
|   This is the original comment
| L james dyer (03/29 08:27)
| |   here is a reply to this comment
| | L james dyer (@3/29 08:27)
| |     and a reply within a reply!!
| L james dyer (03/29 08:28)
|    Here is another reply to the original comment
└────────────────────
&lt;/pre&gt;

&lt;p&gt;
For team collaboration:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-author-list &apos;(&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Alice&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Bob&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Charlie&quot;&lt;/span&gt;))
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-prompt-for-author &apos;threads-only)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-remember-author-per-file t)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Annotations exist at three levels: &lt;code&gt;file&lt;/code&gt; (whole-file overview), &lt;code&gt;defun&lt;/code&gt; (function or block description), and &lt;code&gt;line&lt;/code&gt; (individual elements).  There is also an &lt;code&gt;all&lt;/code&gt; pseudo-level that shows everything at once, which is the default.
&lt;/p&gt;

&lt;p&gt;
Cycle levels with &lt;code&gt;C-c a ]&lt;/code&gt; and &lt;code&gt;C-c a [&lt;/code&gt;.  The header-line shows counts per level (&lt;code&gt;FILE:2 | DEFUN:5 | LINE:3&lt;/code&gt;) with the active level in bold, so you always know where you are, my idea here is to lean towards a coding annotation tool to help teach code or help to remember what has been implemented, so the levels start at a broad file overview and enables you to switch instantly to a more granular level.
&lt;/p&gt;

&lt;p&gt;
The org-mode listing (&lt;code&gt;C-c a l&lt;/code&gt;) gives you a foldable, navigable overview of all annotations in the current file, grouped by level.  Press &lt;code&gt;n&lt;/code&gt; and &lt;code&gt;p&lt;/code&gt; to step through headings, &lt;code&gt;RET&lt;/code&gt; to jump to source.
&lt;/p&gt;

&lt;p&gt;
New in 0.9.6, the tabular listing (&lt;code&gt;C-c a T&lt;/code&gt;) opens a fast, sortable table using &lt;code&gt;tabulated-list-mode&lt;/code&gt; (a feature in Emacs I am starting to leverage more).  Columns for Level, Line, Status, Priority, Comments, Tags, Author, and the first line of the comment.  Click column headers to sort.  This is brilliant for getting a quick birds-eye view of all the open items in a file.
&lt;/p&gt;

&lt;p&gt;
For the global view, &lt;code&gt;simply-annotate-show-all&lt;/code&gt; gathers annotations from every file in the database into a single org-mode buffer.
&lt;/p&gt;

&lt;p&gt;
Enable &lt;code&gt;simply-annotate-dired-mode&lt;/code&gt; and dired buffers show fringe indicators next to files that have annotations.  You can see at a glance which files have notes attached:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(add-hook &apos;dired-mode-hook #&apos;simply-annotate-dired-mode)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Info manuals are also fully supported.  Annotations are tracked per-node, and the listing and jump-to-file commands navigate to Info nodes seamlessly.
&lt;/p&gt;

&lt;p&gt;
Press &lt;code&gt;C-c a e&lt;/code&gt; and you can edit the raw s-expression data structure of any annotation.  Every field is there: thread ID, status, priority, tags, comments with their IDs, parent-IDs, timestamps, and text.  &lt;code&gt;C-c C-c&lt;/code&gt; to save.  This is the escape hatch for when the UI does not quite cover what you need.
&lt;/p&gt;

&lt;p&gt;
Rather than writing paragraphs about how simply-annotate compares to other packages, I have put together a feature matrix in the README.  The short version: if you want threaded conversations, multiple combinable display styles, annotation levels, a smart context-aware command, and zero dependencies in a single file, this is the package for you.  If you need PDF annotation, go with org-noter or org-remark, they are excellent at that.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; simply-annotate
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:bind-keymap&lt;/span&gt; (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c a&quot;&lt;/span&gt; . simply-annotate-command-map)
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:hook&lt;/span&gt; (find-file-hook . simply-annotate-mode))&lt;br&gt;
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-eval-after-load&lt;/span&gt; &apos;simply-annotate
  (add-hook &apos;dired-mode-hook #&apos;simply-annotate-dired-mode))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
The package is available on GitHub on melpa at &lt;a href=&quot;https://melpa.org/#/simply-annotate&quot;&gt;simply-annotate&lt;/a&gt; or  &lt;a href=&quot;https://github.com/captainflasmr/simply-annotate&quot;&gt;https://github.com/captainflasmr/simply-annotate&lt;/a&gt;.  There is also an Info manual if you run &lt;code&gt;M-x info&lt;/code&gt; and search for &lt;code&gt;simply-annotate&lt;/code&gt;.
&lt;/p&gt;
&lt;section id=&quot;outline-container-org265dd7a&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org265dd7a&quot;&gt;Inline Display and the New Pointer&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org265dd7a&quot;&gt;
&lt;p&gt;
Toggle inline display with &lt;code&gt;C-c a /&lt;/code&gt; and annotation content appears as box-drawn blocks directly in your buffer:
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;orgf3ad73c&quot;&gt;
    some annotated code here
    ▴
┌─ ✎ [OPEN/NORMAL] ──────────────
│ This function needs refactoring.
│ The nested conditionals are hard
│ to follow.
└─────────────────────────────────
&lt;/pre&gt;

&lt;p&gt;
New in 0.9.8 is the inline pointer, that little &lt;code&gt;▴&lt;/code&gt; connecting the box to the annotated text.  It is indented to the exact column where the annotation starts, so you always know what the comment refers to.
&lt;/p&gt;

&lt;p&gt;
The fun bit is that the pointer is just a string, and it supports multiline.  So you can customise it to whatever shape you like:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Simple arrow (default)
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-after &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9652;&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-above &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9662;&quot;&lt;/span&gt;)&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Heavy L-bracket (my current favourite)
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-after &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9475;\n&amp;#9495;&amp;#9473;&amp;#9654;&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-above &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9487;&amp;#9473;&amp;#9654;\n&amp;#9475;&quot;&lt;/span&gt;)&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Speech bubble tail
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-after &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot; &amp;#9584;&amp;#9488;&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-above &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot; &amp;#9584;&amp;#9488;&quot;&lt;/span&gt;)&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Decorative diamond
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-after &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9670;&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-inline-pointer-above &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&amp;#9670;&quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
There is a full list of copy-paste options in the Commentary section of the elisp file.  Set to &lt;code&gt;nil&lt;/code&gt; to disable the pointer entirely.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgcc3dc22&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgcc3dc22&quot;&gt;Threaded Conversations&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgcc3dc22&quot;&gt;
&lt;p&gt;
This is where simply-annotate really differs from other annotation packages.  Every annotation is a thread.  You can reply to it with &lt;code&gt;C-c a r&lt;/code&gt;, and replies can be nested under any comment in the thread, not just the root.  It prompts with a hierarchical completing-read menu showing the comment tree.
&lt;/p&gt;

&lt;p&gt;
Each thread has:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;Status&lt;/b&gt; &amp;#x2013; open, in-progress, resolved, closed (&lt;code&gt;C-c a s&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Priority&lt;/b&gt; &amp;#x2013; low, normal, high, critical (&lt;code&gt;C-c a p&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Tags&lt;/b&gt; &amp;#x2013; freeform hashtags for organisation (&lt;code&gt;C-c a t&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Author tracking&lt;/b&gt; &amp;#x2013; configurable per-team, per-file, or single-user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The comment tree renders with box-drawing characters so the hierarchy is always clear:
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;orgc9854ce&quot;&gt;
┌— ® [OPEN/NORMAL] —
| james dyer (03/29 08:27)
|   This is the original comment
| L james dyer (03/29 08:27)
| |   here is a reply to this comment
| | L james dyer (@3/29 08:27)
| |     and a reply within a reply!!
| L james dyer (03/29 08:28)
|    Here is another reply to the original comment
└────────────────────
&lt;/pre&gt;

&lt;p&gt;
For team collaboration:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-author-list &apos;(&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Alice&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Bob&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Charlie&quot;&lt;/span&gt;))
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-prompt-for-author &apos;threads-only)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; simply-annotate-remember-author-per-file t)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Annotations exist at three levels: &lt;code&gt;file&lt;/code&gt; (whole-file overview), &lt;code&gt;defun&lt;/code&gt; (function or block description), and &lt;code&gt;line&lt;/code&gt; (individual elements).  There is also an &lt;code&gt;all&lt;/code&gt; pseudo-level that shows everything at once, which is the default.
&lt;/p&gt;

&lt;p&gt;
Cycle levels with &lt;code&gt;C-c a ]&lt;/code&gt; and &lt;code&gt;C-c a [&lt;/code&gt;.  The header-line shows counts per level (&lt;code&gt;FILE:2 | DEFUN:5 | LINE:3&lt;/code&gt;) with the active level in bold, so you always know where you are, my idea here is to lean towards a coding annotation tool to help teach code or help to remember what has been implemented, so the levels start at a broad file overview and enable you to switch instantly to a more granular level.
&lt;/p&gt;

&lt;p&gt;
The org-mode listing (&lt;code&gt;C-c a l&lt;/code&gt;) gives you a foldable, navigable overview of all annotations in the current file, grouped by level.  Press &lt;code&gt;n&lt;/code&gt; and &lt;code&gt;p&lt;/code&gt; to step through headings, &lt;code&gt;RET&lt;/code&gt; to jump to source.
&lt;/p&gt;

&lt;p&gt;
New in 0.9.6, the tabular listing (&lt;code&gt;C-c a T&lt;/code&gt;) opens a fast, sortable table using &lt;code&gt;tabulated-list-mode&lt;/code&gt; (a feature in Emacs I am starting to leverage more).  Columns for Level, Line, Status, Priority, Comments, Tags, Author, and the first line of the comment.  Click column headers to sort.  This is brilliant for getting a quick birds-eye view of all the open items in a file.
&lt;/p&gt;

&lt;p&gt;
For the global view, &lt;code&gt;simply-annotate-show-all&lt;/code&gt; gathers annotations from every file in the database into a single org-mode buffer.
&lt;/p&gt;

&lt;p&gt;
Enable &lt;code&gt;simply-annotate-dired-mode&lt;/code&gt; and dired buffers show fringe indicators next to files that have annotations.  You can see at a glance which files have notes attached:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(add-hook &apos;dired-mode-hook #&apos;simply-annotate-dired-mode)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Info manuals are also fully supported.  Annotations are tracked per-node, and the listing and jump-to-file commands navigate to Info nodes seamlessly.
&lt;/p&gt;

&lt;p&gt;
Press &lt;code&gt;C-c a e&lt;/code&gt; and you can edit the raw s-expression data structure of any annotation.  Every field is there: thread ID, status, priority, tags, comments with their IDs, parent-IDs, timestamps, and text.  &lt;code&gt;C-c C-c&lt;/code&gt; to save.  This is the escape hatch for when the UI does not quite cover what you need.
&lt;/p&gt;

&lt;p&gt;
Rather than writing paragraphs about how simply-annotate compares to other packages, I have put together a feature matrix in the README.  The short version: if you want threaded conversations, multiple combinable display styles, annotation levels, a smart context-aware command, and zero dependencies in a single file, this is the package for you.  If you need PDF annotation, go with org-noter or org-remark, they are excellent at that.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org186ff8d&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org186ff8d&quot;&gt;Getting Started&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org186ff8d&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; simply-annotate
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:bind-keymap&lt;/span&gt; (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c a&quot;&lt;/span&gt; . simply-annotate-command-map)
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:hook&lt;/span&gt; (find-file-hook . simply-annotate-mode))&lt;br&gt;
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-eval-after-load&lt;/span&gt; &apos;simply-annotate
  (add-hook &apos;dired-mode-hook #&apos;simply-annotate-dired-mode))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
The package is available on GitHub on melpa at &lt;a href=&quot;https://melpa.org/#/simply-annotate&quot;&gt;simply-annotate&lt;/a&gt; or  &lt;a href=&quot;https://github.com/captainflasmr/simply-annotate&quot;&gt;https://github.com/captainflasmr/simply-annotate&lt;/a&gt;.  There is also an Info manual if you run &lt;code&gt;M-x info&lt;/code&gt; and search for &lt;code&gt;simply-annotate&lt;/code&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
</content>
</entry>
<entry>
<title>Ollama Buddy - Seven Lines to Any LLM Provider</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260319143558-emacs--ollama-buddy---create-provider-helper-function/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260319143558-emacs--ollama-buddy---create-provider-helper-function/</id>
<updated>2026-03-19T14:50:00+0000</updated>
<content type="html">&lt;p&gt;
Ever found yourself wanting to add a new AI provider to ollama-buddy? (probably not I would guess 🙂), only to realise you&apos;d need to write an entire Elisp module? Or perhaps you&apos;re running a local inference server that speaks the OpenAI API, but can&apos;t be bothered with the ceremony of creating a dedicated provider file?
&lt;/p&gt;


&lt;div id=&quot;org5f855b4&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/ollama-buddy-logo_001.jpg&quot; alt=&quot;ollama-buddy-logo_001.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
Fair question. That&apos;s exactly why I built &lt;code&gt;ollama-buddy-provider-create&lt;/code&gt; — a single function that lets you register any LLM provider in seconds, whether it&apos;s a cloud API or your own local server.
&lt;/p&gt;

&lt;p&gt;
The traditional approach required a separate &lt;code&gt;.el&lt;/code&gt; file for each provider — OpenAI, Claude, Gemini, you name it. Each with its own &lt;code&gt;defcustom&lt;/code&gt; variables, configuration boilerplate, and maintenance overhead. It worked, but it felt a bit&amp;#x2026; heavy-handed for simple use cases.
&lt;/p&gt;

&lt;p&gt;
What if you just wanted to quickly add support for that new local LM Studio instance running on port 1234? Or point ollama-buddy at your company&apos;s internal AI gateway? Previously, you&apos;d be looking at copying an existing provider file and modifying dozens of lines. Now? One function call.
&lt;/p&gt;

&lt;p&gt;
The magic (yes, of the elisp kind!) happens in &lt;code&gt;ollama-buddy-provider.el&lt;/code&gt;, which provides a generic provider registration system. Instead of requiring separate Elisp files, you can register any provider with a single call:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(ollama-buddy-provider-create
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:name&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;My Local Server&quot;&lt;/span&gt;
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:api-type&lt;/span&gt; &apos;openai
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:endpoint&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;http://localhost:1234/v1/chat&quot;&lt;/span&gt;
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:models-endpoint&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;http://localhost:1234/v1/models&quot;&lt;/span&gt;
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:api-key&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;your-key-here&quot;&lt;/span&gt;
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:prefix&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;l:&quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
&lt;b&gt;Three API types are supported out of the box:&lt;/b&gt;
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;code&gt;openai&lt;/code&gt; (default) — Any OpenAI-compatible chat/completions API&lt;/li&gt;
&lt;li&gt;&lt;code&gt;claude&lt;/code&gt; — Anthropic Claude Messages API&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gemini&lt;/code&gt; — Google Gemini generateContent API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The system handles all the underlying HTTP requests, error mapping, and session management automatically. Your provider just needs to specify which API flavour it speaks.
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;Adding a local LM Studio instance:&lt;/b&gt;
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(ollama-buddy-provider-create
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:name&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;LM Studio&quot;&lt;/span&gt;
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:api-type&lt;/span&gt; &apos;openai
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:endpoint&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;http://localhost:1234/v1/chat/completions&quot;&lt;/span&gt;
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:models-endpoint&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;http://localhost:1234/v1/models&quot;&lt;/span&gt;
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:api-key&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;not-needed&quot;&lt;/span&gt;  &lt;span style=&quot;color: #5B6268;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;LM Studio often doesn&apos;t require auth
&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;:model-prefix&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;l:&quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
&lt;b&gt;Connecting to OpenRouter (400+ models through one API):&lt;/b&gt;
&lt;/p&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(ollama-buddy-provider-create
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:name&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;OpenRouter&quot;&lt;/span&gt;
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:api-type&lt;/span&gt; &apos;openai
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:endpoint&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;https://openrouter.ai/api/v1/chat/completions&quot;&lt;/span&gt;
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:models-endpoint&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;https://openrouter.ai/api/v1/models&quot;&lt;/span&gt;
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:api-key&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;your-openrouter-key&quot;&lt;/span&gt;
 &lt;span style=&quot;color: #fedd38;&quot;&gt;:model-prefix&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;r:&quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
After registration, your new provider appears in the status line and becomes available through the standard model selection interface. The &lt;code&gt;model-prefix&lt;/code&gt; (like &lt;code&gt;l:&lt;/code&gt; for local or &lt;code&gt;r:&lt;/code&gt; for OpenRouter) lets you quickly identify which provider a model belongs to.
&lt;/p&gt;

&lt;p&gt;
The provider system leverages ollama-buddy&apos;s shared infrastructure in &lt;code&gt;ollama-buddy-remote.el&lt;/code&gt;, which extracts common functionality like request handling, error mapping, and response processing. This means your custom provider gets the same robust error handling as the built-in ones:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Proper HTTP status code mapping (rate limits, timeouts, authentication errors)&lt;/li&gt;
&lt;li&gt;Async request support for non-blocking UI&lt;/li&gt;
&lt;li&gt;Automatic model listing and caching&lt;/li&gt;
&lt;li&gt;Integration with the existing session and conversation system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
When you call &lt;code&gt;ollama-buddy-provider-create&lt;/code&gt;, it registers your provider with the core system, making it available to all the usual entry points: the transient menu, model selection, and conversation buffers.
&lt;/p&gt;

&lt;p&gt;
This approach is perfect for:
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Local inference servers (LM Studio, llama.cpp, vLLM, Ollama&apos;s own OpenAI-compat layer)&lt;/li&gt;
&lt;li&gt;Company/internal AI gateways&lt;/li&gt;
&lt;li&gt;Quick experiments with new APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The beauty ojf this system is that it makes ollama-buddy genuinely extensible without requiring deep knowledge of its internals. Want to add support for that new AI service that launched yesterday? You can probably do it in five lines of configuration rather than fifty.
&lt;/p&gt;

&lt;p&gt;
Next up I think this will be the big one, adding tooling for those external providers!!!
&lt;/p&gt;
</content>
</entry>
<entry>
<title>Ollama Buddy - In-Buffer LLM Streaming</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260311185447-emacs--ollama-buddy-in-buffer-replace/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260311185447-emacs--ollama-buddy-in-buffer-replace/</id>
<updated>2026-03-12T11:09:00+0000</updated>
<content type="html">&lt;p&gt;
There is now an in-buffer replace feature in &lt;a href=&quot;https://github.com/captainflasmr/ollama-buddy&quot;&gt;ollama-buddy&lt;/a&gt;, so now an ollama response can work directly on your text, streaming the replacement in real-time, and giving you a simple accept/reject choice!, I have also added an smerge diff inline if desired to show the differences and give the user the ability to accept or reject
&lt;/p&gt;


&lt;div id=&quot;org446cc71&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/ollama-buddy-logo.jpg&quot; alt=&quot;ollama-buddy-logo.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
Here is how it works : &lt;a href=&quot;https://www.youtube.com/watch?v=Po7Wqpk0sqY&quot;&gt;https://www.youtube.com/watch?v=Po7Wqpk0sqY&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
The feature is tucked away in the transient menu. Here&apos;s the workflow:
&lt;/p&gt;

&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Toggle it on via &lt;code&gt;C-c O&lt;/code&gt; → Actions → &lt;code&gt;W&lt;/code&gt;, or run &lt;code&gt;M-x ollama-buddy-toggle-in-buffer-replace&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A small &lt;code&gt;✎&lt;/code&gt; indicator appears in your header line, confirming the mode is active&lt;/li&gt;
&lt;li&gt;Select a region of text you want rewritten&lt;/li&gt;
&lt;li&gt;Invoke a command from the role menu; for example, &lt;code&gt;C-c o&lt;/code&gt; then pick your rewrite command&lt;/li&gt;
&lt;li&gt;Watch as the AI streams the replacement directly into your buffer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
It is also worth noting that each custom menu can be defined with a &lt;code&gt;:destination&lt;/code&gt; option, for either &lt;code&gt;in-buffer&lt;/code&gt; or &lt;code&gt;chat&lt;/code&gt;, so the global in-buffer replace doesn&apos;t need to be selected, and the custom transient menu can be tailored for each command. For example, in the default custom menu, refactor code and proofread have the destination of &lt;code&gt;in-buffer&lt;/code&gt; set, but actions like git commit message or describe code will fall back to the global option, which by default is &lt;code&gt;chat&lt;/code&gt;, which is probably what you would want for these options.
&lt;/p&gt;

&lt;p&gt;
During streaming, you&apos;ll see a dimmed, italic &lt;code&gt;[Rewriting...]&lt;/code&gt; placeholder where your selection was. The new text then flows in with a highlight overlay.
&lt;/p&gt;

&lt;p&gt;
Once the stream finishes, you&apos;re dropped into a minor mode with two options:
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;org35286fd&quot;&gt;
C-c C-c → accept the changes (keep new text, clear highlighting)
C-c C-k → reject and restore your original text
&lt;/pre&gt;

&lt;p&gt;
The mode line helpfully shows &lt;code&gt;[Rewrite?]&lt;/code&gt; while you&apos;re deciding. It&apos;s a bit like &lt;code&gt;ediff&lt;/code&gt; but for AI-generated changes and uses smerge-mode.
&lt;/p&gt;

&lt;p&gt;
What if the AI starts going off the rails halfway through? No problem. Press &lt;code&gt;C-c C-k&lt;/code&gt; at any point during streaming and the network process stops immediately, restoring your original selection. No waiting for it to finish rambling!
&lt;/p&gt;

&lt;p&gt;
Press &lt;code&gt;C-c d&lt;/code&gt; and the original text gets inserted below the new text in the same buffer. Word-level differences are highlighted using &lt;code&gt;smerge-refine-regions&lt;/code&gt;, so you can see exactly what changed at a glance.
&lt;/p&gt;

&lt;p&gt;
Green overlays mark added or modified words. Red strikethrough overlays show what was removed. It&apos;s proper granular diffing, not just a before-and-after comparison.
&lt;/p&gt;

&lt;p&gt;
This workflow is particularly useful for:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Refactoring code blocks you&apos;ve already written&lt;/li&gt;
&lt;li&gt;Rephrasing documentation or prose that feels clunky&lt;/li&gt;
&lt;li&gt;Adjusting tone without losing your core message&lt;/li&gt;
&lt;li&gt;Quick grammar and style improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
It&apos;s less about &quot;write this for me from scratch&quot; and more &quot;help me iterate on what I&apos;ve already got.&quot;
&lt;/p&gt;

&lt;p&gt;
For best results, I&apos;ve found that smaller, focused selections work better than trying to rewrite entire files in one go. The AI has more context to work with, and you can make more granular decisions about what to keep.
&lt;/p&gt;

&lt;p&gt;
Next up is probably some more polish on the diff highlighting, or perhaps exploring how this could work with multi-file projects. But for now, again I think this implementation is good enough.
&lt;/p&gt;
</content>
</entry>
<entry>
<title>Ollama Buddy - Web Search Integration</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260212142000-emacs--web-search-integration-in-ollama-buddy/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260212142000-emacs--web-search-integration-in-ollama-buddy/</id>
<updated>2026-03-04T09:34:00+0000</updated>
<content type="html">&lt;p&gt;
One of the fundamental limitations of local LLMs is their knowledge cutoff - they don&apos;t know about anything that happened after their training data ended. The new web search integration in &lt;a href=&quot;https://github.com/captainflasmr/ollama-buddy&quot;&gt;ollama-buddy&lt;/a&gt; solves this by fetching current information from the web and injecting it into your conversation context.  Ollama has a specific API web search section, so it has now been activated!
&lt;/p&gt;

&lt;p&gt;
Here is a demonstration:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://www.youtube.com/watch?v=05VzAajH404&quot;&gt;https://www.youtube.com/watch?v=05VzAajH404&lt;/a&gt;
&lt;/p&gt;


&lt;div id=&quot;org4a92745&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions.jpg&quot; alt=&quot;20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
The web search feature implements a multi-stage pipeline that transforms search queries into clean, LLM-friendly context, your search query is sent to Ollama&apos;s Web Search API, the API returns structured search results with URLs and snippets.
&lt;/p&gt;

&lt;p&gt;
I have decided that each URL by default is fetched and processed through Emacs&apos; built-in &lt;code&gt;eww&lt;/code&gt; and &lt;code&gt;shr&lt;/code&gt; HTML rendering, but this can of course be configured, set &lt;code&gt;ollama-buddy-web-search-content-source&lt;/code&gt; to control how content is retrieved:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;`eww&apos; (default): Fetch each URL and render through eww/shr for clean text&lt;/li&gt;
&lt;li&gt;`api&apos;: Use content returned directly from Ollama API (faster, less refined)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The &lt;code&gt;shr&lt;/code&gt; (Simple HTML Renderer) library does an excellent job of converting HTML to readable plain text, stripping ads, navigation, and other noise, so I thought why not just use this rather than the return results from the ollama API, as they didn&apos;t seem to be particularly accurate.
&lt;/p&gt;

&lt;p&gt;
The cleaned text is formatted with org headings showing the source URL and attached to your conversation context, so when you send your next prompt, the search results are automatically included in the context. The LLM can now reason about current information as if it had this knowledge all along.
&lt;/p&gt;

&lt;p&gt;
There are multiple ways to search; firstly, is inline &lt;code&gt;@search()&lt;/code&gt; syntax in your prompts (gradually expanding the inline prompting language!), so for example:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;What are the key improvements in @search(Emacs 31 new features)?&lt;br&gt;
Compare @search(Rust async programming) with @search(Go concurrency model)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
ollama-buddy automatically detects these markers, executes the searches, attaches the results, and then sends your prompt, so you can carry out multiple searches.
&lt;/p&gt;

&lt;p&gt;
You can also manual Search and Attach, Use &lt;code&gt;C-c / a&lt;/code&gt; (or &lt;code&gt;M-x ollama-buddy-web-search-attach&lt;/code&gt;)
&lt;/p&gt;

&lt;p&gt;
The search executes, results are attached to your session, and the &lt;code&gt;♁1&lt;/code&gt; indicator appears in the header line and the results can be viewed from the attachments menu, so for example would display something like:
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;orgebcaf53&quot;&gt;
* Web Searches (1)
** latest Emacs 31 features
*** 1. Hide Minor Modes in the Modeline in Emacs 31
*** 2. New Window Commands For Emacs 31
*** 3. Latest version of Emacs (GNU Emacs FAQ)
*** 4. bug#74145: 31.0.50; Default lexical-binding to t
*** 5. New in Emacs 30 (GNU Emacs FAQ)
&lt;/pre&gt;

&lt;p&gt;
with each header foldable, containing the actual search results.
&lt;/p&gt;

&lt;p&gt;
There is a little configuration required to go through the ollama API, first, get an API key from &lt;a href=&quot;https://ollama.com/settings/keys&quot;&gt;https://ollama.com/settings/keys&lt;/a&gt; (it&apos;s free). Then configure:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; ollama-buddy
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:bind&lt;/span&gt;
  (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c o&quot;&lt;/span&gt; . ollama-buddy-role-transient-menu)
  (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c O&quot;&lt;/span&gt; . ollama-buddy-transient-menu-wrapper)
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:custom&lt;/span&gt;
  &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Required: Your Ollama web search API key
&lt;/span&gt;  (ollama-buddy-web-search-api-key &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;your-api-key-here&quot;&lt;/span&gt;))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
For clarification, the content source options are as follows:
&lt;/p&gt;

&lt;p&gt;
The &lt;code&gt;ollama-buddy-web-search-content-source&lt;/code&gt; variable controls how content is retrieved:
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;&lt;code&gt;eww&lt;/code&gt; (default, recommended)&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
Fetches each URL and renders HTML through Emacs&apos; eww/shr. Produces cleaner, more complete content but requires additional HTTP requests.
&lt;/p&gt;

&lt;p&gt;
Pros:
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Much cleaner text extraction&lt;/li&gt;
&lt;li&gt;Full page content, not just snippets&lt;/li&gt;
&lt;li&gt;Removes ads, navigation, clutter&lt;/li&gt;
&lt;li&gt;Works with any website&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Cons:
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Slightly slower (additional HTTP requests)&lt;/li&gt;
&lt;li&gt;Requires network access for each URL&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
&lt;b&gt;&lt;code&gt;api&lt;/code&gt; (experimental)&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
Uses content returned directly from the Ollama API without fetching individual URLs. Faster but content quality depends on what the API provides.
&lt;/p&gt;

&lt;p&gt;
Pros:
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Faster (single API call)&lt;/li&gt;
&lt;li&gt;Less network traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Cons:
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Content may be truncated&lt;/li&gt;
&lt;li&gt;Quality varies by source&lt;/li&gt;
&lt;li&gt;May miss important context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
I strongly recommend sticking with &lt;code&gt;eww&lt;/code&gt; - the quality difference is substantial.
&lt;/p&gt;

&lt;p&gt;
By default, web search fetches up to 5 URLs with 2000 characters per result. This provides rich context without overwhelming the LLM&apos;s context window.
&lt;/p&gt;

&lt;p&gt;
For longer research sessions, you can adjust:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; ollama-buddy-web-search-max-results 10)      &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;More sources
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; ollama-buddy-web-search-snippet-length 5000) &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Longer excerpts&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Be mindful of your LLM&apos;s context window limits. With 5 results at 2000 chars each, you&apos;re adding ~10K characters to your context.
&lt;/p&gt;

&lt;p&gt;
The web search integration fundamentally expands what your local LLMs can do. They&apos;re no longer limited to their training data - they can reach out, fetch current information, and reason about it just like they would with any other context, so hopefully this will now make &lt;code&gt;ollama-buddy&lt;/code&gt; a little more useful
&lt;/p&gt;
</content>
</entry>
<entry>
<title>Ollama Buddy v2.5 - RAG (Retrieval-Augmented Generation) Support</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260224104044-emacs--ollama-buddy-v2.5---rag-(retrieval-augmented-generation)-support/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260224104044-emacs--ollama-buddy-v2.5---rag-(retrieval-augmented-generation)-support/</id>
<updated>2026-02-24T12:10:00+0000</updated>
<content type="html">&lt;p&gt;
One of the things that has always slightly bothered me about chatting with a local LLM is that it only knows what it was trained on (although I suppose most LLMs are like that) . Ask it about your own codebase, your org notes, your project docs - and it&apos;s just guessing. Well, not anymore! Ollama Buddy now ships with proper Retrieval Augmented Generation support built-in
&lt;/p&gt;


&lt;div id=&quot;org03a5837&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions.jpg&quot; alt=&quot;20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;section id=&quot;outline-container-org0064ca4&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org0064ca4&quot;&gt;What even is RAG?&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org0064ca4&quot;&gt;
&lt;p&gt;
If you haven&apos;t come across the term before, the basic idea is simple. Instead of asking the LLM a question cold, you first go off and find the most relevant bits of text from your own documents, then you hand those bits to the LLM along with your question. The LLM now has actual context to work with rather than just vibes. The &quot;retrieval&quot; part is done using vector embeddings - each chunk of your documents gets turned into a mathematical representation, and at query time your question gets the same treatment. Chunks that are mathematically &quot;close&quot; to your question are the ones that get retrieved.
&lt;/p&gt;

&lt;p&gt;
In this case, I have worked to keep the whole pipeline inside Emacs; it talks to Ollama directly to contact an embedding model, which then returns the required information. I have tried to make this as Emacs Org-friendly as possible by storing the embedding information in Org files.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org063821a&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org063821a&quot;&gt;Getting started&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org063821a&quot;&gt;
&lt;p&gt;
You&apos;ll need an embedding model pulled alongside your chat model. The default is &lt;code&gt;nomic-embed-text&lt;/code&gt; which is a solid general-purpose choice:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-sh&quot;&gt;ollama pull nomic-embed-text
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
or just do it within ollama-buddy from the Model Management page.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgf02b355&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgf02b355&quot;&gt;Indexing your documents&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgf02b355&quot;&gt;
&lt;p&gt;
The main entry point is &lt;code&gt;M-x ollama-buddy-rag-index-directory&lt;/code&gt;. Point it at a directory and it will crawl through, chunk everything up, generate embeddings for each chunk, and save an index file. The first time you run this it can take a while depending on how much content you have and how fast your machine is - subsequent updates are much quicker as it only processes changed files.
&lt;/p&gt;

&lt;p&gt;
Supported file types (and I even managed to get pdf text extraction working!):
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Emacs Lisp (&lt;code&gt;.el&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Python, JavaScript, TypeScript, Go, Rust, C/C++, Java, Ruby - basically most languages&lt;/li&gt;
&lt;li&gt;Org-mode and Markdown&lt;/li&gt;
&lt;li&gt;Plain text&lt;/li&gt;
&lt;li&gt;PDF files (if you have &lt;code&gt;pdftotext&lt;/code&gt; from poppler-utils installed)&lt;/li&gt;
&lt;li&gt;YAML, TOML, JSON, HTML, CSS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Files over 1MB are skipped (configurable), and the usual suspects like &lt;code&gt;.git&lt;/code&gt;, &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;__pycache__&lt;/code&gt; are excluded automatically.
&lt;/p&gt;

&lt;p&gt;
The index gets saved into &lt;code&gt;~/.emacs.d/ollama-buddy/rag-indexes/&lt;/code&gt; as a &lt;code&gt;.rag&lt;/code&gt; file named after the directory. You can see what you&apos;ve got with &lt;code&gt;M-x ollama-buddy-rag-list-indexes&lt;/code&gt;.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgd38607a&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgd38607a&quot;&gt;The chunking strategy&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgd38607a&quot;&gt;
&lt;p&gt;
One thing I&apos;m quite happy with here is the chunking. Rather than just splitting on a fixed character count, documents are split into overlapping word-based chunks. The defaults are:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; ollama-buddy-rag-chunk-size 400)    &lt;span style=&quot;color: #5B6268;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;~500 tokens per chunk
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; ollama-buddy-rag-chunk-overlap 50)  &lt;span style=&quot;color: #5B6268;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;50-word overlap between chunks&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
The overlap is important - it means a piece of information that sits right at a chunk boundary doesn&apos;t get lost. Each chunk also tracks its source file and line numbers, so you can see exactly where a result came from.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org8c8faec&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org8c8faec&quot;&gt;Searching and attaching context&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org8c8faec&quot;&gt;
&lt;p&gt;
Once you have an index, there are two main ways to use it:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;code&gt;M-x ollama-buddy-rag-search&lt;/code&gt; - searches and displays the results in a dedicated buffer so you can read through them&lt;/li&gt;

&lt;li&gt;&lt;code&gt;M-x ollama-buddy-rag-attach&lt;/code&gt; - searches and attaches the results directly to your chat context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The second one is the useful one for day-to-day work. After running it, your next chat message will automatically include the retrieved document chunks as context. The status line shows &lt;code&gt;♁N&lt;/code&gt; (where N is the number of attached searches) so you always know what context is in play. Clear everything with &lt;code&gt;M-x ollama-buddy-clear-attachments&lt;/code&gt; or &lt;code&gt;C-c 0&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
You can also trigger searches inline using the &lt;code&gt;@rag()&lt;/code&gt; syntax directly in your prompt and is something fun I have been working on to include an inline command language of sorts, but more about that in a future post.
&lt;/p&gt;

&lt;p&gt;
The similarity search uses cosine similarity with sensible defaults (hopefully!)
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; ollama-buddy-rag-top-k 5)                  &lt;span style=&quot;color: #5B6268;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;return top 5 matching chunks
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; ollama-buddy-rag-similarity-threshold 0.3)  &lt;span style=&quot;color: #5B6268;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;filter out low-relevance results&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Bump &lt;code&gt;top-k&lt;/code&gt; if you want more context, lower the threshold if you&apos;re not getting enough results.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orge9a8835&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orge9a8835&quot;&gt;A practical example&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orge9a8835&quot;&gt;
&lt;p&gt;
Say you&apos;ve been working on a large Emacs package and you want the LLM to help you understand something specific. You&apos;d do:
&lt;/p&gt;

&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;&lt;code&gt;M-x ollama-buddy-rag-index-directory&lt;/code&gt; → point at your project directory&lt;/li&gt;
&lt;li&gt;Wait for indexing to complete (the chat header-line shows progress)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;M-x ollama-buddy-rag-attach&lt;/code&gt; → type your search query, e.g. &quot;streaming filter process&quot;&lt;/li&gt;
&lt;li&gt;Ask your question in the chat buffer as normal&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
The LLM now has the relevant source chunks as context and can give you a much more grounded answer than it would cold.
&lt;/p&gt;

&lt;p&gt;
And the important aspect, especially regarding local models which don&apos;t often have the huge context sizes often found in online LLMs is that it allows for very efficient context retrieval.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org14cc10e&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org14cc10e&quot;&gt;That&apos;s pretty much it!&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org14cc10e&quot;&gt;
&lt;p&gt;
The whole thing is self-contained inside Emacs, no external packages or vector databases, you index once, search as needed, and the LLM gets actual information rather than hallucinating answers about your codebase or anything else that you would want to ingest and it will hopefully make working with local LLMs through ollama noticeably more useful and accurate.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
</content>
</entry>
<entry>
<title>Ollama Buddy v2.0 - LLMs can now call Emacs functions!</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260216084213-emacs--ollama-buddy-v2.0---llms-can-now-call-emacs-functions/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260216084213-emacs--ollama-buddy-v2.0---llms-can-now-call-emacs-functions/</id>
<updated>2026-02-16T08:56:00+0000</updated>
<content type="html">&lt;p&gt;
Tool calling has landed in ollama-buddy!, it&apos;s originally not something I really thought I would end up doing, but as ollama has provided tool enabled models and an API for this feature then I felt obliged to add it. So now LLMs through ollama can now actually do things inside Emacs rather than just talk about them, my original &quot;do things only in the chat buffer and copy and paste&quot; might have gone right out the window in an effort to fully support the ollama API!
&lt;/p&gt;


&lt;div id=&quot;org4737c40&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions.jpg&quot; alt=&quot;20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
What is Tool Calling?
&lt;/p&gt;

&lt;p&gt;
The basic idea is simple: instead of the model only generating text, it can request to invoke functions. You ask &quot;what files are in my project?&quot;, and instead of guessing, the model calls list_directory, gets the real answer, and responds with actual information.
&lt;/p&gt;

&lt;p&gt;
This creates a conversational loop:
&lt;/p&gt;

&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;You send a prompt&lt;/li&gt;
&lt;li&gt;The model decides it needs to call a tool&lt;/li&gt;
&lt;li&gt;ollama-buddy executes the tool and feeds the result back&lt;/li&gt;
&lt;li&gt;The model generates a response using the real data&lt;/li&gt;
&lt;li&gt;Steps 2-4 repeat if more tools are needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
All of this is transparent - you just see the final response in the chat buffer.
&lt;/p&gt;

&lt;p&gt;
The new ollama-buddy-tools.el module ships with 8 built-in tools:
&lt;/p&gt;

&lt;p&gt;
Safe tools (read-only, enabled by default):
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;read_file&lt;/b&gt; - read file contents&lt;/li&gt;
&lt;li&gt;&lt;b&gt;list_directory&lt;/b&gt; - list directory contents&lt;/li&gt;
&lt;li&gt;&lt;b&gt;get_buffer_content&lt;/b&gt; - read an Emacs buffer&lt;/li&gt;
&lt;li&gt;&lt;b&gt;list_buffers&lt;/b&gt; - list open buffers with optional regex filtering&lt;/li&gt;
&lt;li&gt;&lt;b&gt;search_buffer&lt;/b&gt; - regex search within a buffer&lt;/li&gt;
&lt;li&gt;&lt;b&gt;calculate&lt;/b&gt; - evaluate math expressions via calc-eval&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Unsafe tools (require safe mode off):
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;write_file&lt;/b&gt; - write content to files&lt;/li&gt;
&lt;li&gt;&lt;b&gt;execute_shell&lt;/b&gt; - run shell commands&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Safe mode is on by default, so the model can only read - it can&apos;t modify anything unless you explicitly allow it, I think this is quite a nice simple implementation, at the moment I generally have safe mode off but always allowing confirmation for each tool action, but of course you can configure as necessary.
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;Example Session&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
With a tool-capable model like qwen3:8b and tools enabled (C-c W):
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;&amp;gt;&amp;gt; PROMPT: What defuns are defined in ollama-buddy-tools.el?&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
The model calls &lt;b&gt;search_buffer&lt;/b&gt; with a regex pattern, gets the list of function definitions, and gives you a nicely formatted summary. No copy-pasting needed.
&lt;/p&gt;

&lt;p&gt;
&lt;b&gt;Custom Tools&lt;/b&gt;
&lt;/p&gt;

&lt;p&gt;
You can register your own tools with ollama-buddy-tools-register:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;  (ollama-buddy-tools-register
   &apos;my-tool
   &quot;Description of what the tool does&quot;
   &apos;((type . &quot;object&quot;)
     (required . [&quot;param1&quot;])
     (properties . ((param1 . ((type . &quot;string&quot;)
                                (description . &quot;Parameter description&quot;))))))
   (lambda (args)
     (let ((param1 (alist-get &apos;param1 args)))
       (format &quot;Result: %s&quot; param1)))
   t)  ; t = safe tool
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
The registration API takes a name, description, JSON schema for parameters, an implementation function, and a safety flag. The model sees the schema and decides when to call your tool based on the conversation.
&lt;/p&gt;

&lt;p&gt;
A ⚒ symbol now appears next to tool-capable models everywhere - header line, model selector (C-c m), and model management buffer (C-c M). This follows the same pattern as the existing ⊙ vision indicator, so you can see at a glance which models support tools.
&lt;/p&gt;

&lt;p&gt;
That&apos;s it. Pull a tool-capable model (qwen3, llama3.1, mistral, etc.) or use an online tool enabled model from ollama and start chatting. Next up is probably some web searching!, as again the ollama API supports this, so you will be able to pull in the latest from the interwebs to augment your prompt definition!
&lt;/p&gt;
</content>
</entry>
<entry>
<title>Automatically Syncing Emacs Tab Bar Styling With Your Theme</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260211182648-emacs--automatically-syncing-emacs-tab-bar-styling-with-your-theme/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260211182648-emacs--automatically-syncing-emacs-tab-bar-styling-with-your-theme/</id>
<updated>2026-02-11T18:26:00+0000</updated>
<content type="html">&lt;p&gt;
If you&apos;ve ever enabled a new theme and noticed your &lt;b&gt;tab-bar&lt;/b&gt; faces stubbornly hanging onto old colours or custom tweaks, I have found often that the &lt;code&gt;tab-bar&lt;/code&gt;, &lt;code&gt;tab-bar-tab&lt;/code&gt;, and &lt;code&gt;tab-bar-tab-inactive&lt;/code&gt; faces don’t always blend cleanly with freshly loaded themes - especially of the older variety (a bit like me) and especially ones that came out before the tab bar was introduced into Emacs.
&lt;/p&gt;


&lt;div id=&quot;orge7972eb&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20260211182648-emacs--Automatically-Syncing-Emacs-Tab-Bar-Styling-With-Your-Theme.jpg&quot; alt=&quot;20260211182648-emacs--Automatically-Syncing-Emacs-Tab-Bar-Styling-With-Your-Theme.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
So how about a simple solution?, Can I implement something, that whenever I load a theme, the tab-bar faces update based on the theme’s default faces to establish a visually pleasant and coherent look?
&lt;/p&gt;

&lt;p&gt;
Yes, yes I can!; the result is a tiny Elisp enhancement that hooks directly into Emacs&apos; theme-loading process.
&lt;/p&gt;

&lt;p&gt;
Firstly however we need to have a method that will reliably pass over the themes default faces to the tab-bar. Here’s the function that realigns the tab-bar styling with your active theme:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;(defun selected-window-accent-sync-tab-bar-to-theme ()
  &quot;Synchronize tab-bar faces with the current theme.&quot;
  (interactive)
  (let ((default-bg (face-background &apos;default))
        (default-fg (face-foreground &apos;default))
        (inactive-fg (face-foreground &apos;mode-line-inactive)))
    (custom-set-faces
     `(tab-bar ((t (:inherit default :background ,default-bg :foreground ,default-fg))))
     `(tab-bar-tab ((t (:inherit default :background ,default-fg :foreground ,default-bg))))
     `(tab-bar-tab-inactive ((t (:inherit default :background ,default-bg :foreground ,inactive-fg)))))))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
This simply rebuilds the key tab-bar faces so they derive their colours from the current theme’s normal face definitions, so any old themes should now not leave the tab bar faces hanging.
&lt;/p&gt;

&lt;p&gt;
Now for the function activation; Emacs 29 introduced &lt;code&gt;enable-theme-functions&lt;/code&gt;, a hook that runs &lt;b&gt;every time a theme is enabled&lt;/b&gt; - perfect for our use case, but as always I have my eye on older Emacs versions, so lets fall back to a classic approach: advice on &lt;code&gt;load-theme&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
Here’s a version‑aware setup that does the right thing automatically:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;(if (version&amp;lt;= &quot;29.1&quot; emacs-version)
    ;; Emacs 29.1+ - use the official theme hook
    (add-hook &apos;enable-theme-functions
              (lambda (_theme)
                (selected-window-accent-sync-tab-bar-to-theme)))
  ;; Older Emacs - fall back to advising load-theme
  (progn
    (defun selected-window-accent-sync-tab-bar-to-theme--after (&amp;amp;rest _)
      (selected-window-accent-sync-tab-bar-to-theme))
    (advice-add &apos;load-theme :after
                #&apos;selected-window-accent-sync-tab-bar-to-theme--after)))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
With this tweak in place, every time you change themes, your tab-bar instantly updates, colours stay consistent, clean, and theme‑accurate without you having to do anything at all!  The downside to this of course is that any newer themes that were created after the advent of the tab bar in Emacs will have their tab-bar faces overridden, but for me this solution is good enough and gives a pleasant coherent visual tab bar experience.
&lt;/p&gt;

&lt;p&gt;
Yay!, yet another Yak shaved!
&lt;/p&gt;
</content>
</entry>
<entry>
<title>Spent a bit of free time polishing ollama-buddy - github Copilot is now onboard!</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260204100913-emacs--ollama-buddy-updates---github-copilot-integration-plus-others/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260204100913-emacs--ollama-buddy-updates---github-copilot-integration-plus-others/</id>
<updated>2026-02-04T10:40:00+0000</updated>
<content type="html">&lt;p&gt;
I&apos;ve had a little free time recently (figuring out this baby stuff!) and thought I would spend time revisiting and refining my AI assistant &lt;a href=&quot;https://github.com/captainflasmr/ollama-buddy&quot;&gt;ollama-buddy&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
I&apos;ve been playing around with agentic coding and keeping up-to-date on the rapid development of the Emacs AI package landscape and I think I have refined in my own mind my idea of what I would like to see in an Emacs AI assistant.
&lt;/p&gt;

&lt;p&gt;
The headline change regarding the latest release of ollama-buddy is GitHub Copilot integration;  the rest of the work is about smoothing the UI and simplifying day-to-day use.
&lt;/p&gt;


&lt;div id=&quot;orge2b20d8&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions.jpg&quot; alt=&quot;20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
What’s new - the Copilot addition (v1.2)
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;GitHub Copilot Chat API support via a new file, ollama-buddy-copilot.el, so Copilot models can be used alongside your existing providers.&lt;/li&gt;
&lt;li&gt;Authentication uses GitHub’s device flow (OAuth). No API key required: M-x ollama-buddy-copilot-login opens a browser and guides you through secure authentication.&lt;/li&gt;
&lt;li&gt;Copilot models are identified with a &quot;p:&quot; prefix (for example, p:gpt-4o). The header line shows a &quot;p&quot; indicator when the Copilot provider is loaded so you always know it’s available.&lt;/li&gt;
&lt;li&gt;Copilot access exposes a broad set of models from multiple vendors through the Copilot interface: OpenAI (gpt-4o, gpt-5), Anthropic (claude-sonnet-4, claude-opus-4.5), Google (gemini-2.5-pro), and xAI models.&lt;/li&gt;
&lt;li&gt;Quick usage notes:
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Ensure you have an active GitHub Copilot subscription.&lt;/li&gt;
&lt;li&gt;Run M-x ollama-buddy-copilot-login.&lt;/li&gt;
&lt;li&gt;Enter the device code in your browser at github.com/login/device when prompted.&lt;/li&gt;
&lt;li&gt;Select a Copilot model with C-c m (e.g., p:gpt-4o).&lt;/li&gt;
&lt;/ol&gt;&lt;/li&gt;
&lt;li&gt;Example config to load Copilot support:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;(use-package ollama-buddy
  :bind
  (&quot;C-c o&quot; . ollama-buddy-menu)
  (&quot;C-c O&quot; . ollama-buddy-transient-menu-wrapper)
  :config
  (require &apos;ollama-buddy-copilot nil t))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Other notable updates in this release series
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;v1.2.1 (2026-02-02)&lt;/b&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Attachment count indicator on the header line so you get a constant visual reminder that the session has attachments.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;v1.1.5 (2026-01-31)&lt;/b&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Global system prompt feature (enabled by default): sets a baseline set of instructions (for example, to prefer plain prose and avoid markdown tables) that is prepended to session-specific system prompts. This helps keep responses consistent across providers and things like malformed markdown tables for example, which seems to be common. There’s a toggle (ollama-buddy-global-system-prompt-enabled) and a quick command to flip it (ollama-buddy-toggle-global-system-prompt), plus a transient-menu entry.&lt;/li&gt;
&lt;li&gt;Consolidated model management: streamlined into a single model management buffer (C-c W) and the welcome screen now points to that buffer for model tasks.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;v1.1.4 (2026-01-31)&lt;/b&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Header-line and keybinding cleanup: C-c RET to send prompts (matches gptel, as I feel this seems intuitive), removed a redundant backend indicator, shortened the markdown indicator to &quot;MD&quot;, and fixed markdown → org heading conversion to keep structure sane.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;v1.1.3 (2026-01-31)&lt;/b&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Chat UX improvements and simplification: added ollama-buddy-auto-scroll (default nil - don’t auto-scroll so you can read while streaming) and ollama-buddy-pulse-response (flashes the response on completion, taking from gptel again, as if there is no autoscrolling it is useful to visually see when the response has completed). Removed the model name coloring feature and related toggles to simplify code and improve org-mode performance.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;v1.1.2 (2026-01-30)&lt;/b&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Streamlined welcome screen and model selection, clearer provider indicators in the header line and an improved list of enabled online LLM providers.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>Ollama buddy now supports cloud models!</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260128082917-emacs--ollama-buddy-now-supports-cloud-models/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260128082917-emacs--ollama-buddy-now-supports-cloud-models/</id>
<updated>2026-01-28T08:29:00+0000</updated>
<content type="html">&lt;p&gt;
Having another look at my AI assistant - ollama-buddy, its been a while and it seems ollama has moved on since I started creating this package last year, so I have developed a new roadmap and the first step is to add ollama cloud models!
&lt;/p&gt;


&lt;div id=&quot;orgdac0279&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions.jpg&quot; alt=&quot;20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
Here are some references to the project, including a youtube channel where I upload ollama-buddy demonstrations:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://melpa.org/#/ollama-buddy&quot;&gt;https://melpa.org/#/ollama-buddy&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://github.com/captainflasmr/ollama-buddy&quot;&gt;https://github.com/captainflasmr/ollama-buddy&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
Here is the changelog for the cloud model implementation:
&lt;/p&gt;
&lt;section id=&quot;outline-container-org55af4eb&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org55af4eb&quot;&gt;&lt;span class=&quot;timestamp-wrapper&quot;&gt;&lt;span class=&quot;timestamp&quot;&gt;&amp;lt;2026-01-28 Wed&amp;gt; &lt;/span&gt;&lt;/span&gt; &lt;b&gt;1.1&lt;/b&gt;&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org55af4eb&quot;&gt;
&lt;p&gt;
Added Ollama Cloud Models support
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Cloud models (running on ollama.com infrastructure) now work seamlessly&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ollama-buddy-cloud-signin&lt;/code&gt; to automatically open browser for authentication&lt;/li&gt;
&lt;li&gt;Cloud models are proxied through the local Ollama server which handles authentication&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;C-u C-c m&lt;/code&gt; or transient menu &quot;Model &amp;gt; Cloud&quot; to select cloud models&lt;/li&gt;
&lt;li&gt;Status line shows ☁ indicator when using a cloud model&lt;/li&gt;
&lt;li&gt;Available cloud models include: qwen3-coder:480b-cloud, deepseek-v3.1:671b-cloud, gpt-oss:120b-cloud, minimax-m2.1:cloud, and more&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/section&gt;
</content>
</entry>
<entry>
<title>Auto-Populating Weekly Dates in Org-Mode Tables</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260126101317-emacs--auto-populating-weekly-dates-in-org-mode-tables/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260126101317-emacs--auto-populating-weekly-dates-in-org-mode-tables/</id>
<updated>2026-01-26T10:13:00+0000</updated>
<content type="html">&lt;p&gt;
Here is just a quick one, I was working with an org-mode table for tracking work weeks and needed to auto-populate a Date column where each row increments by exactly one week. The table structure looked like this:
&lt;/p&gt;


&lt;div id=&quot;org85608ac&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20260126101317-emacs--Auto-Populating-Weekly-Dates-in-Org-Mode-Tables.jpg&quot; alt=&quot;20260126101317-emacs--Auto-Populating-Weekly-Dates-in-Org-Mode-Tables.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
The first row has a base date (2026-01-05), and I wanted subsequent rows to automatically calculate as weekly increments: 2026-01-12, 2026-01-19, and so on.
&lt;/p&gt;

&lt;p&gt;
Initially, I tried several approaches that seemed logical but encountered &lt;code&gt;#ERROR&lt;/code&gt; results and eventually settled on a working solution which is to hardcode the base date directly in the formula:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-org&quot;&gt;&lt;span style=&quot;color: #686b78;&quot;&gt;#+TBLFM: $3=&apos;(format-time-string &quot;%Y-%m-%d&quot; (time-add (date-to-time &quot;2026-01-05&quot;) (* (- @# 2) 7 24 3600)))&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
which gave:
&lt;/p&gt;


&lt;div id=&quot;org2de80a1&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20260126101317-emacs--Auto-Populating-Weekly-Dates-in-Org-Mode-Tables2.jpg&quot; alt=&quot;20260126101317-emacs--Auto-Populating-Weekly-Dates-in-Org-Mode-Tables2.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
and I can now of course extend the table for all the weeks in the year and I don&apos;t have to fill in manually any more!
&lt;/p&gt;

&lt;p&gt;
Here&apos;s how it works:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;code&gt;(date-to-time &quot;2026-01-05&quot;)&lt;/code&gt; - Convert the hardcoded base date to Emacs time format&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(- @# 2)&lt;/code&gt; - Calculate the offset from the base row&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(* (- @# 2) 7 24 3600)&lt;/code&gt; - Convert the offset to seconds (weeks × days × hours × seconds)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;time-add&lt;/code&gt; - Add the offset to the base date&lt;/li&gt;
&lt;li&gt;&lt;code&gt;format-time-string &quot;%Y-%m-%d&quot;&lt;/code&gt; - Format back to ISO date string&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>Speed Reading in Emacs: Building an RSVP Reader</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260116182841-emacs--speed-reading-in-emacs-building-an-rsvp-reader/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260116182841-emacs--speed-reading-in-emacs-building-an-rsvp-reader/</id>
<updated>2026-01-18T10:30:00+0000</updated>
<content type="html">&lt;p&gt;
I recently came across a fascinating video titled &quot;How Fast Can You Read? - Speed Reading Challenge&quot; that demonstrated the power of RSVP (Rapid Serial Visual Presentation) for speed reading. The concept  is quite nice and simple and I vaguely remember seeing something about it a few years back.  Instead of your eyes scanning across lines of text, words are presented one at a time in a fixed position. This eliminates the mechanical overhead of eye movements and can dramatically increase reading speed!
&lt;/p&gt;


&lt;div id=&quot;org0e7d199&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20260116182841-emacs--Speed-Reading-in-Emacs-Building-an-RSVP-Reader.jpg&quot; alt=&quot;20260116182841-emacs--Speed-Reading-in-Emacs-Building-an-RSVP-Reader.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
So, I immediately wondered, could I build this into Emacs?, actually no, firstly I thought, are there any packages for Emacs that can do this?, of course there are!, the &lt;b&gt;spray&lt;/b&gt; package from MELPA is a more mature, feature-rich option if you&apos;re looking for production-ready RSVP reading in Emacs, and also there is &lt;b&gt;speedread&lt;/b&gt;.  However, there&apos;s something satisfying about having a compact, single-function solution that does exactly what you need, so lets see if I can build one!
&lt;/p&gt;

&lt;p&gt;
RSVP works by displaying words sequentially in the same location on screen. Your eyes remain stationary, focused on a single point, while words flash by at a controlled pace. This technique can boost reading speeds to 300-600+ words per minute, compared to typical reading speeds of 200-300 WPM.
&lt;/p&gt;

&lt;p&gt;
The key innovation is the &lt;b&gt;Optimal Recognition Point (ORP)&lt;/b&gt; - typically positioned about one-third into each word. This is where your eye naturally fixates when reading. By aligning each word&apos;s ORP at the same screen position, RSVP creates an optimal visual flow.
&lt;/p&gt;

&lt;p&gt;
Given Emacs&apos; extensive text processing capabilities, this sounds something that Emacs could eat for breakfast. Here is what I came up with:
&lt;/p&gt;

&lt;p&gt;
Here is a quick video of my implementation:
&lt;/p&gt;


&lt;div id=&quot;org8feba76&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20260118100321--screen-recording.gif&quot; alt=&quot;20260118100321--screen-recording.gif&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
and the defun:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;rsvp-minibuffer&lt;/span&gt; ()
  &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Display words from point (or mark to point) in minibuffer using RSVP.
Use f/s for speed, [/] for size, b/n to skip, SPC to pause, q to quit.&quot;&lt;/span&gt;
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;interactive&lt;/span&gt;)
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let*&lt;/span&gt; ((start (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; (region-active-p) (region-beginning) (point)))
         (end (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; (region-active-p) (region-end) (point-max)))
         (text (buffer-substring-no-properties start end))
         (wpm 350) (font-size 200) (orp-column 20)
         (word-positions &apos;()) (pos 0) (i 0)
         (message-log-max nil))  &lt;span style=&quot;color: #5B6268;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Disable message logging
&lt;/span&gt;    &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Build word positions list
&lt;/span&gt;    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;dolist&lt;/span&gt; (word (split-string text))
      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;unless&lt;/span&gt; (string-blank-p word)
        (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when-let&lt;/span&gt; ((word-start (string-match (regexp-quote word) text pos)))
          (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;push&lt;/span&gt; (cons word (+ start word-start)) word-positions)
          (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; pos (+ word-start (length word))))))
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; word-positions (nreverse word-positions))
    &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Display loop
&lt;/span&gt;    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;while&lt;/span&gt; (&amp;lt; i (length word-positions))
      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let*&lt;/span&gt; ((word (car (nth i word-positions)))
             (word-pos (cdr (nth i word-positions)))
             (word-len (length word))
             (delay (* (/ 60.0 wpm)
                      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;cond&lt;/span&gt; ((&amp;lt; word-len 3) 0.8) ((&amp;gt; word-len 8) 1.3) (t 1.0))
                      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; (string-match-p &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;[.!?]$&quot;&lt;/span&gt; word) 1.5 1.0)))
             (orp-pos (/ word-len 3))
             (face-mono `(&lt;span style=&quot;color: #fedd38;&quot;&gt;:height&lt;/span&gt; ,font-size &lt;span style=&quot;color: #fedd38;&quot;&gt;:family&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;monospace&quot;&lt;/span&gt;))
             (face-orp `(&lt;span style=&quot;color: #fedd38;&quot;&gt;:foreground&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;red&quot;&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;:weight&lt;/span&gt; normal ,@face-mono))
             (padded-word (concat 
                          (propertize (make-string (max 0 (- orp-column orp-pos)) ?\s) &apos;face face-mono)
                          (propertize (substring word 0 orp-pos) &apos;face face-mono)
                          (propertize (substring word orp-pos (1+ orp-pos)) &apos;face face-orp)
                          (propertize (substring word (1+ orp-pos)) &apos;face face-mono))))
        (goto-char (+ word-pos word-len))
        (message &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%s&quot;&lt;/span&gt; padded-word)
        (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;pcase&lt;/span&gt; (read-event nil nil delay)
          (?f (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; wpm (min 1000 (+ wpm 50))))
          (?s (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; wpm (max 50 (- wpm 50))))
          (?\[ (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; font-size (max 100 (- font-size 20))))
          (?\] (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; font-size (min 400 (+ font-size 20))))
          (?b (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; i (max 0 (- i 10))))
          (?n (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; i (min (1- (length word-positions)) (+ i 10))))
          (?\s (read-event (format &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%s [PAUSED - WPM: %d]&quot;&lt;/span&gt; padded-word wpm)))
          (?q (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; i (length word-positions)))
          (_ (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; i (1+ i))))))))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
The function calculates the ORP as one-third through each word and highlights it in red. By padding each word with spaces, the ORP character stays perfectly aligned in the same column, creating that crucial stationary focal point.
&lt;/p&gt;

&lt;p&gt;
To ensure pixel-perfect alignment, the function explicitly sets a monospace font family for all displayed text. Without this, proportional fonts would cause the ORP to drift slightly between words, although I think at times there is a little waddle, but it is good enough.
&lt;/p&gt;

&lt;p&gt;
Also, Not all words are created equal:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Short words (&amp;lt; 3 characters) display 20% faster&lt;/li&gt;
&lt;li&gt;Long words (&amp;gt; 8 characters) display 30% slower&lt;/li&gt;
&lt;li&gt;Words ending in punctuation (&lt;code&gt;.!?&lt;/code&gt;) get 50% more time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
This mimics natural reading rhythms where you&apos;d naturally pause at sentence boundaries.
&lt;/p&gt;

&lt;p&gt;
While reading, you can try these keybindings: (which I borrowed off &lt;b&gt;spray&lt;/b&gt;)
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;code&gt;f&lt;/code&gt; / &lt;code&gt;s&lt;/code&gt; - Speed up or slow down (±50 WPM)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[&lt;/code&gt; / &lt;code&gt;]&lt;/code&gt; - Decrease or increase font size&lt;/li&gt;
&lt;li&gt;&lt;code&gt;b&lt;/code&gt; / &lt;code&gt;n&lt;/code&gt; - Skip backward or forward by 10 words&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SPC&lt;/code&gt; - Pause (press any key to resume)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;q&lt;/code&gt; - Quit&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C-g&lt;/code&gt; - Emergency quit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Also The function tracks each word&apos;s position in the original buffer and updates &lt;code&gt;point&lt;/code&gt; as you read. This means:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;You can see where you are in the text&lt;/li&gt;
&lt;li&gt;When you quit, your cursor is at the last word you read&lt;/li&gt;
&lt;li&gt;You can resume reading by running the function again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
To use it, simply:
&lt;/p&gt;

&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Position your cursor where you want to start reading (or select a region)&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;M-x rsvp-minibuffer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Watch the words flow in the minibuffer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
The function works from point to end of buffer, or if you have an active region, it only processes the selected text.
&lt;/p&gt;

&lt;p&gt;
If you&apos;re curious about RSVP reading, drop this function into your Emacs config and give it a try. Start at 300-350 WPM and see how it feels. You might be surprised at how much faster you can consume text when your eyes aren&apos;t constantly moving across the page.
&lt;/p&gt;

&lt;p&gt;
The code is simple enough to customize - adjust the default WPM, change the ORP colour, modify the timing multipliers, or add new controls. That&apos;s the beauty of Emacs, if you can imagine it, you can build it.
&lt;/p&gt;
</content>
</entry>
<entry>
<title>A single function ripgrep alternative to rgrep</title>
<link href="https://emacs.dyerdwelling.family/emacs/20260109094340-emacs--a-single-function-ripgrep-alternative-to-rgrep/"/>
<id>https://emacs.dyerdwelling.family/emacs/20260109094340-emacs--a-single-function-ripgrep-alternative-to-rgrep/</id>
<updated>2026-01-09T09:43:00+0000</updated>
<content type="html">&lt;p&gt;
For years, &lt;code&gt;rgrep&lt;/code&gt; has been the go-to solution for searching codebases in Emacs. It&apos;s built-in, reliable, and works everywhere. But it&apos;s slow on large projects and uses the aging &lt;code&gt;find&lt;/code&gt; and &lt;code&gt;grep&lt;/code&gt; commands.
&lt;/p&gt;

&lt;p&gt;
Packages like &lt;code&gt;deadgrep&lt;/code&gt; and &lt;code&gt;rg.el&lt;/code&gt; provide ripgrep integration, and for years I used &lt;code&gt;deadgrep&lt;/code&gt; and really liked it. But what if you could get ripgrep&apos;s speed with just a single function you paste into your config?
&lt;/p&gt;


&lt;div id=&quot;org8e3c2d0&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20260109094340-emacs--A-Single-Function-ripgrep-Alternative-to-rgrep.jpg&quot; alt=&quot;20260109094340-emacs--A-Single-Function-ripgrep-Alternative-to-rgrep.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
This post introduces a ~100 line &lt;code&gt;defun&lt;/code&gt; that replaces rgrep, no packages, no dependencies, just pure Elisp. It&apos;s fast, asynchronous, works offline, and mimics rgrep&apos;s familiar interface so it can leverage &lt;code&gt;grep-mode&lt;/code&gt;
&lt;/p&gt;

&lt;p&gt;
So, why not just use rgrep?
&lt;/p&gt;

&lt;p&gt;
I think that rgrep has three main limitations:
&lt;/p&gt;

&lt;p&gt;
Firstly, speed. On a project with 10,000+ files, rgrep can take 15-30 seconds. Ripgrep completes the same search in under a second.
&lt;/p&gt;

&lt;p&gt;
Secondly, file ignoring, rgrep requires manually configuring &lt;code&gt;grep-find-ignored-directories&lt;/code&gt; or &lt;code&gt;grep-find-ignored-files&lt;/code&gt;, I had the following typical configuration for rgrep, but it wasn&apos;t as flexible as I would like it to be:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(eval-after-load &apos;grep
  &apos;(progn
     (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;dolist&lt;/span&gt; (dir &apos;(&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;nas&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;.cache&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;cache&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;elpa&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;chromium&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;.local/share&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;syncthing&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;.mozilla&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;.local/lib&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Games&quot;&lt;/span&gt;))
       (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;push&lt;/span&gt; dir grep-find-ignored-directories))
     (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;dolist&lt;/span&gt; (file &apos;(&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;.cache&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;*cache*&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;*.iso&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;*.xmp&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;*.jpg&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;*.mp4&quot;&lt;/span&gt;))
       (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;push&lt;/span&gt; file grep-find-ignored-files))
     ))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Ripgrep automatically respects an &lt;code&gt;.ignore&lt;/code&gt; file. Just create an &lt;code&gt;.ignore&lt;/code&gt; file in your project root and list patterns to exclude, this is just a simple text file, universally applied across all searches and any changes can be easily applied.
&lt;/p&gt;

&lt;p&gt;
Thirdly, modern features. Ripgrep includes smart-case search, better regex support, and automatic binary file detection. Of course, there is a context that can be displayed around the found line, but in order to get ripgrep to work with grep-mode, this is not really doable, and it&apos;s not something I need anyway.
&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;
Here is the complete ripgrep implementation that you can paste directly into your &lt;code&gt;init.el&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;my/grep&lt;/span&gt; (search-term &lt;span style=&quot;color: #ee7b29;&quot;&gt;&amp;amp;optional&lt;/span&gt; directory glob)
  &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Run ripgrep (rg) with SEARCH-TERM and optionally DIRECTORY and GLOB.
If ripgrep is unavailable, fall back to Emacs&apos;s rgrep command. Highlights SEARCH-TERM in results.
By default, only the SEARCH-TERM needs to be provided. If called with a
universal argument, DIRECTORY and GLOB are prompted for as well.&quot;&lt;/span&gt;
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;interactive&lt;/span&gt;
   (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let*&lt;/span&gt; ((univ-arg current-prefix-arg)
          (default-search-term
           (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;cond&lt;/span&gt;
            ((use-region-p)
             (buffer-substring-no-properties (region-beginning) (region-end)))
            ((thing-at-point &apos;symbol t))
            ((thing-at-point &apos;word t))
            (t &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt;))))
     (list
      (read-string (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; (string-empty-p default-search-term)
                       &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Search for: &quot;&lt;/span&gt;
                     (format &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Search for (default `%s`): &quot;&lt;/span&gt; default-search-term))
                   nil nil default-search-term)
      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; univ-arg (read-directory-name &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Directory: &quot;&lt;/span&gt;))
      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; univ-arg (read-string &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;File pattern (glob, default: ): &quot;&lt;/span&gt; nil nil &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt;)))))
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let*&lt;/span&gt; ((directory (expand-file-name (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;or&lt;/span&gt; directory default-directory)))
         (glob (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;or&lt;/span&gt; glob &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt;))
         (buffer-name &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;*grep*&quot;&lt;/span&gt;))
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; (executable-find &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;rg&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((buffer (get-buffer-create buffer-name)))
          (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-current-buffer&lt;/span&gt; buffer
            (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; default-directory directory)
            (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((inhibit-read-only t))
              (erase-buffer)
              (insert (format &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;-*- mode: grep; default-directory: \&quot;%s\&quot; -*-\n\n&quot;&lt;/span&gt; directory))
              (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; (not (string= &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt; glob))
                  (insert (format &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;[o] Glob: %s\n\n&quot;&lt;/span&gt; glob)))
              (insert &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Searching...\n\n&quot;&lt;/span&gt;))
            (grep-mode)
            (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq-local&lt;/span&gt; my/grep-search-term search-term)
            (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq-local&lt;/span&gt; my/grep-directory directory)
            (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq-local&lt;/span&gt; my/grep-glob glob))&lt;br&gt;
          (pop-to-buffer buffer)
          (goto-char (point-min))&lt;br&gt;
          (make-process
           &lt;span style=&quot;color: #fedd38;&quot;&gt;:name&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;ripgrep&quot;&lt;/span&gt;
           &lt;span style=&quot;color: #fedd38;&quot;&gt;:buffer&lt;/span&gt; buffer
           &lt;span style=&quot;color: #fedd38;&quot;&gt;:command&lt;/span&gt; `(&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;rg&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;--color=never&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;--max-columns=500&quot;&lt;/span&gt; 
                      &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;--column&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;--line-number&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;--no-heading&quot;&lt;/span&gt; 
                      &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;--smart-case&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;-e&quot;&lt;/span&gt; ,search-term
                      &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;--glob&quot;&lt;/span&gt; ,glob ,directory)
           &lt;span style=&quot;color: #fedd38;&quot;&gt;:filter&lt;/span&gt; (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;lambda&lt;/span&gt; (proc string)
                     (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; (buffer-live-p (process-buffer proc))
                       (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-current-buffer&lt;/span&gt; (process-buffer proc)
                         (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((inhibit-read-only t)
                               (moving (= (point) (process-mark proc))))
                           (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; string (replace-regexp-in-string &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;[\r\0\x01-\x08\x0B-\x0C\x0E-\x1F]&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt; string))
                           &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Replace full directory path with ./ in the incoming output
&lt;/span&gt;                           (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; string (replace-regexp-in-string 
                                         (concat &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;^&quot;&lt;/span&gt; (regexp-quote directory))
                                         &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;./&quot;&lt;/span&gt;
                                         string))
                           (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;save-excursion&lt;/span&gt;
                             (goto-char (process-mark proc))
                             (insert string)
                             (set-marker (process-mark proc) (point)))
                           (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; moving (goto-char (process-mark proc)))))))
           &lt;span style=&quot;color: #fedd38;&quot;&gt;:sentinel&lt;/span&gt;
           (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;lambda&lt;/span&gt; (proc _event)
             (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; (memq (process-status proc) &apos;(exit signal))
               (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-current-buffer&lt;/span&gt; (process-buffer proc)
                 (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((inhibit-read-only t))
                   &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Remove &quot;Searching...&quot; line
&lt;/span&gt;                   (goto-char (point-min))
                   (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;while&lt;/span&gt; (re-search-forward &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Searching\\.\\.\\.\n\n&quot;&lt;/span&gt; nil t)
                     (replace-match &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt; nil t))&lt;br&gt;
                   &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Clean up the output - replace full paths with ./
&lt;/span&gt;                   (goto-char (point-min))
                   (forward-line 3)
                   (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let&lt;/span&gt; ((start-pos (point)))
                     (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;while&lt;/span&gt; (re-search-forward (concat &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;^&quot;&lt;/span&gt; (regexp-quote directory)) nil t)
                       (replace-match &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;./&quot;&lt;/span&gt; t t))&lt;br&gt;
                     &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Check if any results were found
&lt;/span&gt;                     (goto-char start-pos)
                     (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; (= (point) (point-max))
                       (insert &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;No results found.\n&quot;&lt;/span&gt;)))&lt;br&gt;
                   (goto-char (point-max))
                   (insert &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;\nRipgrep finished\n&quot;&lt;/span&gt;)&lt;br&gt;
                   &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Highlight search terms using grep&apos;s match face
&lt;/span&gt;                   (goto-char (point-min))
                   (forward-line 3)
                   (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;save-excursion&lt;/span&gt;
                     (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;while&lt;/span&gt; (re-search-forward (regexp-quote search-term) nil t)
                       (put-text-property (match-beginning 0) (match-end 0)
                                          &apos;face &apos;match)
                       (put-text-property (match-beginning 0) (match-end 0)
                                          &apos;font-lock-face &apos;match))))&lt;br&gt;
                 &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Set up keybindings
&lt;/span&gt;                 (local-set-key (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;D&quot;&lt;/span&gt;) 
                                (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;lambda&lt;/span&gt; () 
                                  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;interactive&lt;/span&gt;)
                                  (my/grep my/grep-search-term 
                                           (read-directory-name &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;New search directory: &quot;&lt;/span&gt;)
                                           my/grep-glob)))
                 (local-set-key (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;S&quot;&lt;/span&gt;) 
                                (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;lambda&lt;/span&gt; () 
                                  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;interactive&lt;/span&gt;)
                                  (my/grep (read-string &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;New search term: &quot;&lt;/span&gt;
                                                        nil nil my/grep-search-term)
                                           my/grep-directory
                                           my/grep-glob)))
                 (local-set-key (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;o&quot;&lt;/span&gt;) 
                                (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;lambda&lt;/span&gt; () 
                                  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;interactive&lt;/span&gt;)
                                  (my/grep my/grep-search-term
                                           my/grep-directory
                                           (read-string &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;New glob: &quot;&lt;/span&gt;))))
                 (local-set-key (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;g&quot;&lt;/span&gt;) 
                                (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;lambda&lt;/span&gt; () 
                                  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;interactive&lt;/span&gt;)
                                  (my/grep my/grep-search-term my/grep-directory my/grep-glob)))&lt;br&gt;
                 (goto-char (point-min))
                 (message &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;ripgrep finished.&quot;&lt;/span&gt;))))
           )
          (message &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;ripgrep started...&quot;&lt;/span&gt;))
      &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Fallback to rgrep
&lt;/span&gt;      (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;progn&lt;/span&gt;
        (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; default-directory directory)
        (message (format &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%s : %s : %s&quot;&lt;/span&gt; search-term glob directory))
        (rgrep search-term (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; (string= &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt; glob) &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;*&quot;&lt;/span&gt; glob) directory)))))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
That&apos;s it. ~100 lines. No dependencies. No packages to manage! (well except ripgrep of course)
&lt;/p&gt;

&lt;p&gt;
Now that I have complete control over this function, I have added further improvements over rgrep, inspired by &lt;code&gt;deadgrep&lt;/code&gt;
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;S&lt;/code&gt;&lt;/b&gt; - New search term&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;D&lt;/code&gt;&lt;/b&gt; - New directory&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;o&lt;/code&gt;&lt;/b&gt; - New glob pattern&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;code&gt;g&lt;/code&gt;&lt;/b&gt; - Re-run current search&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
and a universal argument can be passed through to set these up on the initial grep
&lt;/p&gt;

&lt;p&gt;
I have tried to make the output as similar as possible to rgrep, to be compatible with &lt;code&gt;grep-mode&lt;/code&gt; and for familiarity, so it will be something like:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-nil&quot;&gt;-*- mode: grep; default-directory: &quot;~/project/&quot; -*-&lt;br&gt;
[o] Glob: *.el&lt;br&gt;
./init.el:42:10:(defun my-function ()
./config.el:156:5:  (my-function)
./helpers.el:89:12:;; Helper for my-function&lt;br&gt;
Ripgrep finished
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
and if a glob is applied it will display the glob pattern.
&lt;/p&gt;

&lt;p&gt;
Its perfect for offline environments, and yes, I&apos;m banging on about this again!, no network, no package manager, no dependencies (except ripgrep of course!)
&lt;/p&gt;
</content>
</entry>
<entry>
<title>New package dired-video-thumbnail added to MELPA!</title>
<link href="https://emacs.dyerdwelling.family/emacs/20251231183401-emacs--dired-video-thumbnail/"/>
<id>https://emacs.dyerdwelling.family/emacs/20251231183401-emacs--dired-video-thumbnail/</id>
<updated>2025-12-31T18:34:00+0000</updated>
<content type="html">&lt;p&gt;
I have created another package!, this time something that I thought was missing from the mighty Emacs and that is the ability to show video thumbnails in a grid and to be able to filter, sort e.t.c.  Basically like an enhanced &lt;code&gt;image-dired&lt;/code&gt;.  I have been increasingly using &lt;code&gt;image-dired&lt;/code&gt; for my image editing and management needs and am always adding little improvements, to such an extent I decided to create a video thumb grid package, enjoy!
&lt;/p&gt;


&lt;div id=&quot;org13be4f6&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20251231183401-emacs--dired-video-thumbnail.jpg&quot; alt=&quot;20251231183401-emacs--dired-video-thumbnail.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;section id=&quot;outline-container-orgfd0472a&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgfd0472a&quot;&gt;Introduction&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgfd0472a&quot;&gt;
&lt;p&gt;
&lt;code&gt;dired-video-thumbnail&lt;/code&gt; is an Emacs package that provides &lt;code&gt;image-dired&lt;/code&gt; style thumbnail viewing for video files. It extracts thumbnails from videos using &lt;code&gt;ffmpeg&lt;/code&gt; and displays them in a grid layout, allowing you to visually browse and manage video collections directly from Emacs.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org2a7a0af&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org2a7a0af&quot;&gt;Features&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org2a7a0af&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;Thumbnail grid display&lt;/b&gt; - View video thumbnails in a configurable grid layout&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Persistent caching&lt;/b&gt; - Thumbnails are cached and only regenerated when the source file changes&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Async generation&lt;/b&gt; - Emacs remains responsive while thumbnails are generated in the background&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dired integration&lt;/b&gt; - Marks sync bidirectionally with the associated dired buffer&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Visual mark indication&lt;/b&gt; - Marked thumbnails display a coloured border (like &lt;code&gt;image-dired&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dynamic header line&lt;/b&gt; - Shows filename, dimensions, duration, and file size for the current video&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Click to play&lt;/b&gt; - Open videos in your preferred external player&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cross-platform&lt;/b&gt; - Works on Linux, macOS, and Windows&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Resizable thumbnails&lt;/b&gt; - Adjust thumbnail size on the fly&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Sorting&lt;/b&gt; - Sort videos by name, date, size, or duration&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Filtering&lt;/b&gt; - Filter videos by name pattern, duration range, or file size&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Recursive search&lt;/b&gt; - Browse videos across subdirectories with optional auto-recursive mode&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Transient menu&lt;/b&gt; - Comprehensive command menu accessible via &lt;code&gt;.&lt;/code&gt; or &lt;code&gt;C-c .&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgd4078b4&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgd4078b4&quot;&gt;Whats New&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgd4078b4&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org8db3327&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org8db3327&quot;&gt;&lt;span class=&quot;timestamp-wrapper&quot;&gt;&lt;span class=&quot;timestamp&quot;&gt;&amp;lt;2025-12-15 Mon&amp;gt; &lt;/span&gt;&lt;/span&gt; 0.3.0&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org8db3327&quot;&gt;
&lt;p&gt;
Added transient menu interface
&lt;/p&gt;

&lt;p&gt;
Introduced a comprehensive transient menu (&lt;code&gt;dired-video-thumbnail-transient&lt;/code&gt;) providing quick access to all commands via &lt;code&gt;.&lt;/code&gt; or &lt;code&gt;C-c .&lt;/code&gt; in the thumbnail buffer. The menu displays current state (sort order, filters, video count, recursive/wrap mode) and organises commands into logical groups: Navigation, Playback, Sorting, Filtering, Marking, Delete, Display, and Other.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org7576ffa&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org7576ffa&quot;&gt;&lt;span class=&quot;timestamp-wrapper&quot;&gt;&lt;span class=&quot;timestamp&quot;&gt;&amp;lt;2025-12-15 Mon&amp;gt; &lt;/span&gt;&lt;/span&gt; 0.2.0&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org7576ffa&quot;&gt;
&lt;p&gt;
Enhanced package with sorting, filtering, and docs
&lt;/p&gt;

&lt;p&gt;
Added sorting and filtering features to &lt;code&gt;dired-video-thumbnail&lt;/code&gt;. Introduced customizable options for sorting and filtering criteria, and implement interactive commands for toggling these settings. Included comprehensive documentation in Texinfo format, covering installation, usage, and customization.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org636d239&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org636d239&quot;&gt;Requirements&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org636d239&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Emacs 28.1 or later&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ffmpeg.org/&quot;&gt;ffmpeg&lt;/a&gt; and &lt;code&gt;ffprobe&lt;/code&gt; installed and available in your PATH&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/magit/transient&quot;&gt;transient&lt;/a&gt; 0.4.0 or later (for the transient menu)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orge6e5ec0&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orge6e5ec0&quot;&gt;Installation&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orge6e5ec0&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org9bb4a2d&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org9bb4a2d&quot;&gt;Manual&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org9bb4a2d&quot;&gt;
&lt;p&gt;
Download &lt;code&gt;dired-video-thumbnail.el&lt;/code&gt; and place it in your load-path:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(add-to-list &apos;load-path &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/path/to/dired-video-thumbnail/&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;require&lt;/span&gt; &apos;&lt;span style=&quot;color: #ee7b29;&quot;&gt;dired-video-thumbnail&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org52ce475&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org52ce475&quot;&gt;use-package&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org52ce475&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; dired-video-thumbnail
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:load-path&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/path/to/dired-video-thumbnail/&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:bind&lt;/span&gt; (&lt;span style=&quot;color: #fedd38;&quot;&gt;:map&lt;/span&gt; dired-mode-map
         (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-t v&quot;&lt;/span&gt; . dired-video-thumbnail)))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org3a34030&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org3a34030&quot;&gt;straight.el&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org3a34030&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(straight-use-package
 &apos;(dired-video-thumbnail &lt;span style=&quot;color: #fedd38;&quot;&gt;:type&lt;/span&gt; git &lt;span style=&quot;color: #fedd38;&quot;&gt;:host&lt;/span&gt; github &lt;span style=&quot;color: #fedd38;&quot;&gt;:repo&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;captainflasmr/dired-video-thumbnail&quot;&lt;/span&gt;))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org48a86e6&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org48a86e6&quot;&gt;Usage&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org48a86e6&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org29c1a83&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org29c1a83&quot;&gt;Basic Usage&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org29c1a83&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Open a directory containing video files in dired&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;M-x dired-video-thumbnail&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A new buffer opens displaying thumbnails for all videos in the directory&lt;/li&gt;
&lt;li&gt;The cursor automatically moves to the first thumbnail&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orge06ee65&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orge06ee65&quot;&gt;With Marked Files&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orge06ee65&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;In dired, mark specific video files with &lt;code&gt;m&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;M-x dired-video-thumbnail&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Only thumbnails for the marked videos are displayed&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgb9d86a4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgb9d86a4&quot;&gt;Recursive Mode&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgb9d86a4&quot;&gt;
&lt;p&gt;
To include videos from subdirectories:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Use &lt;code&gt;C-u M-x dired-video-thumbnail&lt;/code&gt; (with prefix argument)&lt;/li&gt;
&lt;li&gt;Or run &lt;code&gt;M-x dired-video-thumbnail-recursive&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Or press &lt;code&gt;R&lt;/code&gt; in the thumbnail buffer to toggle recursive mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
When &lt;code&gt;dired-video-thumbnail-auto-recursive&lt;/code&gt; is enabled (the default), the package automatically searches subdirectories if the current directory contains no video files.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org61c304f&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org61c304f&quot;&gt;Suggested Keybinding&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org61c304f&quot;&gt;
&lt;p&gt;
Add a keybinding in dired for quick access:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-eval-after-load&lt;/span&gt; &apos;dired
  (define-key dired-mode-map (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-t v&quot;&lt;/span&gt;) #&apos;dired-video-thumbnail))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org3ec1934&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org3ec1934&quot;&gt;Transient Menu&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org3ec1934&quot;&gt;
&lt;p&gt;
Press &lt;code&gt;.&lt;/code&gt; or &lt;code&gt;C-c .&lt;/code&gt; in the thumbnail buffer to open the transient menu. This provides a comprehensive interface to all commands with a live status display.
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;orgf40c78b&quot;&gt;
State: Sort: name ↑ | Videos: 42 | Recursive: OFF | Wrap: ON

Navigation            Playback       Sorting              Filtering
n Next                RET Play video s Sort menu...       / Filter menu...
p Previous            o Play video   S Interactive sort   \ Interactive filter
C-n Next row                         r Reverse order      c Clear filters
C-p Previous row
d Go to dired

Marking               Delete         Display              Other
m Mark menu...        D Delete       v Display menu...    g Regenerate thumbnail
M Mark all            x Delete marked+ Larger thumbnails  G Regenerate all
U Unmark all                         - Smaller thumbnails C Clear cache
t Toggle all marks                   w Toggle wrap        ? Help
                                     R Toggle recursive   q Quit menu
                                                          Q Quit buffer
&lt;/pre&gt;

&lt;p&gt;
The status line at the top shows:
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Current sort criteria and direction (e.g., &lt;code&gt;name ↑&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Number of videos displayed (and total if filtered)&lt;/li&gt;
&lt;li&gt;Recursive mode status&lt;/li&gt;
&lt;li&gt;Wrap display mode status&lt;/li&gt;
&lt;li&gt;Active filters (if any)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgfa39cd3&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgfa39cd3&quot;&gt;Submenus&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgfa39cd3&quot;&gt;
&lt;p&gt;
Several keys open submenus with additional options:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;code&gt;s&lt;/code&gt; - &lt;b&gt;Sort menu&lt;/b&gt;: Sort by name, date, size, or duration; reverse order&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/&lt;/code&gt; - &lt;b&gt;Filter menu&lt;/b&gt;: Filter by name regexp, duration range, or size range; clear filters&lt;/li&gt;
&lt;li&gt;&lt;code&gt;m&lt;/code&gt; - &lt;b&gt;Mark menu&lt;/b&gt;: Mark/unmark current, toggle current, mark/unmark/toggle all&lt;/li&gt;
&lt;li&gt;&lt;code&gt;v&lt;/code&gt; - &lt;b&gt;Display menu&lt;/b&gt;: Adjust size, toggle wrap/recursive, refresh, regenerate thumbnails&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org17b31e9&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org17b31e9&quot;&gt;Header Line&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org17b31e9&quot;&gt;
&lt;p&gt;
As you navigate between thumbnails, the header line dynamically displays information about the current video:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;Mark indicator&lt;/b&gt; - A red &lt;code&gt;*&lt;/code&gt; if the video is marked&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Filename&lt;/b&gt; - The video filename in bold&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dimensions&lt;/b&gt; - Video resolution (e.g., &lt;code&gt;1920x1080&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Duration&lt;/b&gt; - Video length (e.g., &lt;code&gt;5:32&lt;/code&gt; or &lt;code&gt;1:23:45&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;File size&lt;/b&gt; - Size in MB (e.g., &lt;code&gt;45.2 MB&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The header also shows current sort settings (e.g., &lt;code&gt;[name ↑]&lt;/code&gt;), active filters, and a &lt;code&gt;[recursive]&lt;/code&gt; indicator when browsing subdirectories.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org0ad6a7c&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org0ad6a7c&quot;&gt;Keybindings&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org0ad6a7c&quot;&gt;
&lt;p&gt;
In the &lt;code&gt;*Video Thumbnails*&lt;/code&gt; buffer:
&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org3bdeaf5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org3bdeaf5&quot;&gt;Transient Menu&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org3bdeaf5&quot;&gt;
&lt;table class=&quot;table table-striped&quot; border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;


&lt;colgroup&gt;
&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Key&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Command&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-transient&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Open transient menu&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;C-c .&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-transient&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Open transient menu&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org2d664a6&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org2d664a6&quot;&gt;Navigation&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org2d664a6&quot;&gt;
&lt;table class=&quot;table table-striped&quot; border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;


&lt;colgroup&gt;
&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Key&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Command&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;n&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-next&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Move to next thumbnail&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;p&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-previous&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Move to previous thumbnail&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;SPC&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-play&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Play video at point&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;C-f&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-forward&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Move to next thumbnail&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;C-b&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-backward&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Move to previous thumbnail&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;&amp;lt;right&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-forward&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Move to next thumbnail&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;&amp;lt;left&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-backward&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Move to previous thumbnail&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;&amp;lt;up&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-previous-row&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Move up one row&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;&amp;lt;down&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-next-row&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Move down one row&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;d&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-goto-dired&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Switch to associated dired buffer&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;q&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;quit-window&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Close the thumbnail buffer&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;Q&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-quit-and-kill&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Quit and kill the buffer&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org3340821&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org3340821&quot;&gt;Playback&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org3340821&quot;&gt;
&lt;table class=&quot;table table-striped&quot; border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;


&lt;colgroup&gt;
&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Key&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Command&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;RET&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-play&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Play video at point&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;o&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-play&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Play video at point&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;mouse-1&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-play&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Play video (click)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
On Linux, videos open with &lt;code&gt;xdg-open&lt;/code&gt;. On macOS, they open with &lt;code&gt;open&lt;/code&gt;. On Windows, they open with the system default player. You can also specify a custom player.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org5c05c9b&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org5c05c9b&quot;&gt;Marking&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org5c05c9b&quot;&gt;
&lt;p&gt;
Marks are synchronised with the associated dired buffer, so marking a video in the thumbnail view also marks it in dired, and vice versa.
&lt;/p&gt;

&lt;table class=&quot;table table-striped&quot; border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;


&lt;colgroup&gt;
&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Key&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Command&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;m&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-mark&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Mark video and move to next&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;u&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-unmark&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Unmark video and move to next&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;mouse-3&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-toggle-mark&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Toggle mark (right-click)&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;M&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-mark-all&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Mark all videos&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;U&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-unmark-all&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Unmark all videos&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;t&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-toggle-all-marks&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Invert all marks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org1314474&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org1314474&quot;&gt;Deletion&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org1314474&quot;&gt;
&lt;table class=&quot;table table-striped&quot; border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;


&lt;colgroup&gt;
&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Key&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Command&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;D&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-delete&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Delete video at point (with confirmation)&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;x&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-delete-marked&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Delete marked videos (with confirmation)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgb0425fd&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgb0425fd&quot;&gt;Display&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgb0425fd&quot;&gt;
&lt;table class=&quot;table table-striped&quot; border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;


&lt;colgroup&gt;
&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Key&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Command&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;+&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-increase-size&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Increase thumbnail size&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;-&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-decrease-size&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Decrease thumbnail size&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;r&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-refresh&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Refresh the display&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;w&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-toggle-wrap&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Toggle wrap mode (flow vs fixed cols)&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;R&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-toggle-recursive&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Toggle recursive directory search&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;g&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-regenerate&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Regenerate thumbnail at point&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;G&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-regenerate-all&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Regenerate all thumbnails&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgb77746f&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgb77746f&quot;&gt;Sorting&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgb77746f&quot;&gt;
&lt;table class=&quot;table table-striped&quot; border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;


&lt;colgroup&gt;
&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Key&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Command&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;S&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-sort&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Interactive sort menu&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;sn&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-sort-by-name&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Sort by filename&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;sd&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-sort-by-date&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Sort by modification date&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;ss&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-sort-by-size&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Sort by file size&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;sD&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-sort-by-duration&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Sort by video duration&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;sr&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-sort-reverse&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Reverse sort order&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org756ff22&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org756ff22&quot;&gt;Filtering&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org756ff22&quot;&gt;
&lt;table class=&quot;table table-striped&quot; border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;


&lt;colgroup&gt;
&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Key&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Command&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;\&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-filter&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Interactive filter menu&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;/n&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-filter-by-name&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Filter by name regexp&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;/d&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-filter-by-duration&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Filter by duration range&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;/s&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-filter-by-size&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Filter by size range&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;/c&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-filter-clear&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Clear all filters&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;//&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-filter-clear&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Clear all filters&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgde46f7d&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgde46f7d&quot;&gt;Help&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgde46f7d&quot;&gt;
&lt;table class=&quot;table table-striped&quot; border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;


&lt;colgroup&gt;
&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Key&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Command&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;h&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-help&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Show help&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;?&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;&lt;code&gt;dired-video-thumbnail-help&lt;/code&gt;&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Show help&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org4aa2f0f&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org4aa2f0f&quot;&gt;Customisation&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org4aa2f0f&quot;&gt;
&lt;p&gt;
All customisation options are in the &lt;code&gt;dired-video-thumbnail&lt;/code&gt; group. Use &lt;code&gt;M-x customize-group RET dired-video-thumbnail RET&lt;/code&gt; to browse them interactively.
&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org60da207&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org60da207&quot;&gt;Thumbnail Cache Location&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org60da207&quot;&gt;
&lt;p&gt;
Thumbnails are stored in &lt;code&gt;~/.emacs.d/dired-video-thumbnails/&lt;/code&gt; by default:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-cache-dir &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;~/path/to/cache/&quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org03ee869&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org03ee869&quot;&gt;Thumbnail Size&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org03ee869&quot;&gt;
&lt;p&gt;
Control the generated thumbnail size and display height:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-size 200)           &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Generated thumbnail size (pixels)
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-display-height 150) &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Display height in buffer&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Thumbnails are generated as squares to ensure consistent grid alignment regardless of video aspect ratio.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgaf9dd26&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgaf9dd26&quot;&gt;Grid Layout&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgaf9dd26&quot;&gt;
&lt;p&gt;
Set the number of columns in the thumbnail grid:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-columns 4)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org3f2666b&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org3f2666b&quot;&gt;Wrap Display&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org3f2666b&quot;&gt;
&lt;p&gt;
Control whether thumbnails wrap to fill the window width:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-wrap-display t)   &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Wrap to window width (default)
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-wrap-display nil) &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Use fixed columns
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-spacing 4)        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Spacing between thumbnails (pixels)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org8898ca1&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org8898ca1&quot;&gt;Thumbnail Timestamp&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org8898ca1&quot;&gt;
&lt;p&gt;
By default, thumbnails are extracted at 5 seconds into the video. Change this to get a more representative frame:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-timestamp &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;00:00:10&quot;&lt;/span&gt;)  &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;10 seconds in
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-timestamp &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;00:01:00&quot;&lt;/span&gt;)  &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;1 minute in
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-timestamp nil)         &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Let ffmpeg choose&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgdb8914c&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgdb8914c&quot;&gt;Video Player&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgdb8914c&quot;&gt;
&lt;p&gt;
Set your preferred video player:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-video-player &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;mpv&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-video-player &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;vlc&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-video-player nil)  &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Use system default&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
When set to &lt;code&gt;nil&lt;/code&gt; (the default), videos open with:
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Linux: &lt;code&gt;xdg-open&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;open&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Windows: System default player (e.g., Films &amp;amp; TV)&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orga689d97&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orga689d97&quot;&gt;Video Extensions&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orga689d97&quot;&gt;
&lt;p&gt;
Add or modify recognised video file extensions:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-video-extensions
      &apos;(&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;mp4&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;mkv&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;avi&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;mov&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;webm&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;m4v&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;wmv&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;flv&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;mpeg&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;mpg&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;ogv&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;3gp&quot;&lt;/span&gt;))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org54625a2&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org54625a2&quot;&gt;Mark Border Appearance&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org54625a2&quot;&gt;
&lt;p&gt;
Marked thumbnails are indicated with a coloured border. Customise the border width and colour:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-mark-border-width 4)  &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Border width in pixels
&lt;/span&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Change border colour via the face
&lt;/span&gt;(set-face-foreground &apos;dired-video-thumbnail-mark &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;blue&quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgd75749f&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgd75749f&quot;&gt;Default Sorting&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgd75749f&quot;&gt;
&lt;p&gt;
Set the default sort criteria and order:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-sort-by &apos;name)       &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Options: name, date, size, duration
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-sort-order &apos;ascending) &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Options: ascending, descending&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org3bc0daa&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org3bc0daa&quot;&gt;Recursive Search&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org3bc0daa&quot;&gt;
&lt;p&gt;
Control recursive directory searching behaviour:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-recursive nil)       &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Always search recursively
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-auto-recursive t)    &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Auto-recursive when no local videos (default)&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
When &lt;code&gt;dired-video-thumbnail-auto-recursive&lt;/code&gt; is enabled and the current directory has no video files but has subdirectories, the package automatically searches recursively.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org17b7402&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org17b7402&quot;&gt;ffmpeg Path&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org17b7402&quot;&gt;
&lt;p&gt;
If ffmpeg/ffprobe are not in your PATH:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-ffmpeg-program &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/usr/local/bin/ffmpeg&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dired-video-thumbnail-ffprobe-program &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/usr/local/bin/ffprobe&quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgba8d467&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgba8d467&quot;&gt;Example Configuration&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgba8d467&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; dired-video-thumbnail
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:load-path&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/path/to/dired-video-thumbnail/&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:bind&lt;/span&gt; (&lt;span style=&quot;color: #fedd38;&quot;&gt;:map&lt;/span&gt; dired-mode-map
         (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-t v&quot;&lt;/span&gt; . dired-video-thumbnail))
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:custom&lt;/span&gt;
  (dired-video-thumbnail-size 250)
  (dired-video-thumbnail-display-height 180)
  (dired-video-thumbnail-columns 5)
  (dired-video-thumbnail-timestamp &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;00:00:10&quot;&lt;/span&gt;)
  (dired-video-thumbnail-video-player nil)  &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Use system default
&lt;/span&gt;  (dired-video-thumbnail-mark-border-width 5)
  (dired-video-thumbnail-sort-by &apos;date)
  (dired-video-thumbnail-sort-order &apos;descending)
  (dired-video-thumbnail-auto-recursive t)
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:custom-face&lt;/span&gt;
  (dired-video-thumbnail-mark ((t (&lt;span style=&quot;color: #fedd38;&quot;&gt;:foreground&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;orange&quot;&lt;/span&gt;)))))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org2026243&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org2026243&quot;&gt;Cache Management&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org2026243&quot;&gt;
&lt;p&gt;
Thumbnails are cached based on the file path and modification time. If you modify a video file, the thumbnail will be automatically regenerated on next view.
&lt;/p&gt;

&lt;p&gt;
Video metadata (dimensions, duration) is also cached in memory to avoid repeated calls to &lt;code&gt;ffprobe&lt;/code&gt;.
&lt;/p&gt;

&lt;p&gt;
To manually clear the thumbnail cache:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;M-x dired-video-thumbnail-clear-cache
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orga7a23a6&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orga7a23a6&quot;&gt;Workflow Examples&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orga7a23a6&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgbeeba68&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgbeeba68&quot;&gt;Reviewing and Deleting Unwanted Videos&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgbeeba68&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Open a directory with videos in dired&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C-t v&lt;/code&gt; to open thumbnail view&lt;/li&gt;
&lt;li&gt;Browse thumbnails with &lt;code&gt;n&lt;/code&gt;, &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;SPC&lt;/code&gt;, or arrow keys&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;D&lt;/code&gt; to delete individual videos, or mark with &lt;code&gt;m&lt;/code&gt; and delete with &lt;code&gt;x&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgf935d5a&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgf935d5a&quot;&gt;Selecting Videos for Processing&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgf935d5a&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Open thumbnail view with &lt;code&gt;C-t v&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Mark videos you want to process with &lt;code&gt;m&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;d&lt;/code&gt; to switch to dired&lt;/li&gt;
&lt;li&gt;Your marked videos are already selected in dired&lt;/li&gt;
&lt;li&gt;Use any dired command (&lt;code&gt;C&lt;/code&gt;, &lt;code&gt;R&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt;, etc.) on marked files&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org9029f56&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org9029f56&quot;&gt;Quick Video Preview&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org9029f56&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;In dired, position cursor on a video file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;C-t v&lt;/code&gt; opens thumbnail view&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RET&lt;/code&gt; to play the video&lt;/li&gt;
&lt;li&gt;&lt;code&gt;q&lt;/code&gt; to return to dired&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org35e0bd0&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org35e0bd0&quot;&gt;Finding Large Videos&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org35e0bd0&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Open thumbnail view with &lt;code&gt;C-t v&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;.&lt;/code&gt; to open the transient menu&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;s&lt;/code&gt; then &lt;code&gt;s&lt;/code&gt; to sort by size&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;r&lt;/code&gt; to reverse order (largest first)&lt;/li&gt;
&lt;li&gt;Or use &lt;code&gt;/&lt;/code&gt; then &lt;code&gt;s&lt;/code&gt; to filter by size range&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org9d60e60&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org9d60e60&quot;&gt;Finding Long Videos&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org9d60e60&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Press &lt;code&gt;.&lt;/code&gt; to open the transient menu&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;s&lt;/code&gt; then &lt;code&gt;D&lt;/code&gt; to sort by duration&lt;/li&gt;
&lt;li&gt;Or use &lt;code&gt;/&lt;/code&gt; then &lt;code&gt;d&lt;/code&gt; to filter by duration range (e.g., &lt;code&gt;5:00&lt;/code&gt; to &lt;code&gt;30:00&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org0b0e227&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org0b0e227&quot;&gt;Searching by Name&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org0b0e227&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Press &lt;code&gt;.&lt;/code&gt; to open the transient menu&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;/&lt;/code&gt; then &lt;code&gt;n&lt;/code&gt; and enter a regexp pattern&lt;/li&gt;
&lt;li&gt;Only matching videos are shown&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;c&lt;/code&gt; to clear the filter&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgfa548bc&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgfa548bc&quot;&gt;Troubleshooting&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgfa548bc&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org7ea86f4&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org7ea86f4&quot;&gt;Thumbnails not generating&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org7ea86f4&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Ensure ffmpeg is installed: &lt;code&gt;ffmpeg -version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check that ffmpeg is in your PATH or set &lt;code&gt;dired-video-thumbnail-ffmpeg-program&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Try regenerating with &lt;code&gt;g&lt;/code&gt; on a specific thumbnail&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgcab3714&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgcab3714&quot;&gt;Placeholder showing instead of thumbnail&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgcab3714&quot;&gt;
&lt;p&gt;
Some videos may fail to generate thumbnails if:
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;The video is corrupted&lt;/li&gt;
&lt;li&gt;The timestamp is beyond the video duration (try setting &lt;code&gt;dired-video-thumbnail-timestamp&lt;/code&gt; to &lt;code&gt;nil&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;ffmpeg doesn&apos;t support the codec&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
Press &lt;code&gt;g&lt;/code&gt; on the thumbnail to retry generation.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org20a3632&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org20a3632&quot;&gt;Video info not showing in header line&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org20a3632&quot;&gt;
&lt;p&gt;
Ensure &lt;code&gt;ffprobe&lt;/code&gt; is installed (it comes with ffmpeg). Set &lt;code&gt;dired-video-thumbnail-ffprobe-program&lt;/code&gt; if it&apos;s not in your PATH.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgc29a8b8&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgc29a8b8&quot;&gt;Marks not syncing with dired&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgc29a8b8&quot;&gt;
&lt;p&gt;
Run &lt;code&gt;M-x dired-video-thumbnail-debug&lt;/code&gt; to check if the dired buffer is properly associated. The output should show a live dired buffer reference.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org70b0d81&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org70b0d81&quot;&gt;Performance with many videos&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org70b0d81&quot;&gt;
&lt;p&gt;
The package processes up to 4 videos concurrently by default. For directories with hundreds of videos, initial thumbnail generation may take some time, but Emacs remains responsive and thumbnails appear as they complete.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org1e455a1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org1e455a1&quot;&gt;Related Packages&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org1e455a1&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/emacs/Image_002dDired.html&quot;&gt;image-dired&lt;/a&gt; - Built-in image thumbnail browser for dired&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/alexluigit/dirvish&quot;&gt;dirvish&lt;/a&gt; - A modern file manager for Emacs with preview support&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/section&gt;
</content>
</entry>
<entry>
<title>Setting Up Emacs for C# Development on Windows</title>
<link href="https://emacs.dyerdwelling.family/emacs/20251216082551-emacs--setting-up-emacs-for-c#-development-on-windows/"/>
<id>https://emacs.dyerdwelling.family/emacs/20251216082551-emacs--setting-up-emacs-for-c#-development-on-windows/</id>
<updated>2025-12-16T08:25:00+0000</updated>
<content type="html">&lt;section id=&quot;outline-container-orgec7eed1&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgec7eed1&quot;&gt;Introduction&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgec7eed1&quot;&gt;
&lt;p&gt;
I have been developing C# with .NET 9.0 for the last year on Windows and I thought it was probably time to write down my current setup, and maybe someone might even find this useful!
&lt;/p&gt;

&lt;p&gt;
So, this guide documents my setup for running Emacs 30.1 on Windows with full C# development support, including LSP, debugging (through DAPE), and all the ancillary tools you&apos;d expect from a modern development environment. The setup is designed to be portable and self-contained, which is particularly useful in air-gapped or restricted environments.
&lt;/p&gt;

&lt;p&gt;
A version of this can be found at &lt;a href=&quot;https://github.com/captainflasmr/Emacs-on-windows&quot;&gt;https://github.com/captainflasmr/Emacs-on-windows&lt;/a&gt; which will be a living continually updated version!
&lt;/p&gt;


&lt;div id=&quot;orgcb47680&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20251216082551-emacs--Setting-Up-Emacs-for-Csharp-Development-on-Windows.jpg&quot; alt=&quot;20251216082551-emacs--Setting-Up-Emacs-for-Csharp-Development-on-Windows.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgd82550d&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgd82550d&quot;&gt;Prerequisites&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgd82550d&quot;&gt;
&lt;p&gt;
Before we begin, you&apos;ll need:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;Windows 10 or 11&lt;/b&gt; (64-bit)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;.NET 9.0 SDK&lt;/b&gt; - Required for csharp-ls and building .NET projects&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Visual Studio 2022&lt;/b&gt; (optional) - Useful for MSBuild and if you need the full IDE occasionally&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Administrator access&lt;/b&gt; - For initial setup only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
You can verify your .NET installation by opening a command prompt and running:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-shell&quot;&gt;dotnet --version
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
If you see version 9.0.x or later, you&apos;re ready to proceed.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgb6f11f4&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgb6f11f4&quot;&gt;The Big Picture&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgb6f11f4&quot;&gt;
&lt;p&gt;
Here&apos;s what we&apos;re building:
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;orgd02b3f3&quot;&gt;
D:\source\emacs-30.1\
├── bin\
│   ├── emacs.exe, runemacs.exe, etc.
│   ├── PortableGit\          # Git for version control
│   ├── Apache-Subversion\    # SVN (if needed)
│   ├── csharp-ls\            # C# Language Server
│   ├── netcoredbg\           # .NET debugger
│   ├── omnisharp-win-x64\    # Alternative C# LSP
│   ├── hunspell\             # Spell checking
│   ├── find\                 # ripgrep for fast searching
│   ├── ffmpeg-7.1.1-.../     # Video processing
│   └── ImageMagick-.../      # Image processing
└── share\
    └── emacs\...
&lt;/pre&gt;

&lt;p&gt;
The key insight here is keeping everything within the Emacs installation directory. This makes the whole setup portable-you can copy it to another machine or keep it on a USB drive.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org7cfbaed&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org7cfbaed&quot;&gt;Step 1: Installing Emacs&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org7cfbaed&quot;&gt;
&lt;p&gt;
Download Emacs 30.1 from the &lt;a href=&quot;https://www.gnu.org/software/emacs/download.html&quot;&gt;GNU Emacs download page&lt;/a&gt;. For Windows, grab the installer or the zip archive.
&lt;/p&gt;

&lt;p&gt;
I install to an external drive &lt;code&gt;D:\source\emacs-30.1&lt;/code&gt; rather than Program Files-it avoids permission issues and keeps everything in one place.
&lt;/p&gt;

&lt;p&gt;
Test your installation by running &lt;code&gt;bin\runemacs.exe&lt;/code&gt;. You should see a fresh Emacs frame.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgf3fad19&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgf3fad19&quot;&gt;Step 2: Setting Up csharp-ls (The C# Language Server)&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgf3fad19&quot;&gt;
&lt;p&gt;
This is the heart of the C# development experience. &lt;code&gt;csharp-ls&lt;/code&gt; provides code completion, go-to-definition, find references, diagnostics, and more through the Language Server Protocol (LSP).
&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orge72d52d&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orge72d52d&quot;&gt;Option A: Installing via dotnet tool (Recommended for Internet Access)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orge72d52d&quot;&gt;
&lt;p&gt;
If you have internet access, the easiest way to install csharp-ls is as a .NET global tool:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-shell&quot;&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Install the latest version globally
&lt;/span&gt;dotnet tool install --global csharp-ls&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Or install a specific version
&lt;/span&gt;dotnet tool install --global csharp-ls --version 0.20.0&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Verify installation
&lt;/span&gt;csharp-ls --version
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
By default, global tools are installed to:
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Windows: &lt;code&gt;%USERPROFILE%\.dotnet\tools&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The executable will be &lt;code&gt;csharp-ls.exe&lt;/code&gt; and can be called directly once the tools directory is in your PATH.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgd7adefd&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgd7adefd&quot;&gt;Option B: Offline Installation via NuGet Package&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgd7adefd&quot;&gt;
&lt;p&gt;
For air-gapped environments, you can download the NuGet package and extract it manually:
&lt;/p&gt;

&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;&lt;p&gt;
On a machine with internet, download the package:
&lt;/p&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-shell&quot;&gt;   &lt;span style=&quot;color: #5B6268;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Download the nupkg file
&lt;/span&gt;   nuget install csharp-ls -Version 0.20.0 -OutputDirectory ./packages&lt;br&gt;
   &lt;span style=&quot;color: #5B6268;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Or download directly from NuGet Gallery:
&lt;/span&gt;   &lt;span style=&quot;color: #5B6268;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;https://www.nuget.org/packages/csharp-ls/
&lt;/span&gt;   &lt;span style=&quot;color: #5B6268;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Click &quot;Download package&quot; on the right side&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;
The &lt;code&gt;.nupkg&lt;/code&gt; file is just a ZIP archive. Extract it:
&lt;/p&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-shell&quot;&gt;   &lt;span style=&quot;color: #5B6268;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Rename to .zip and extract, or use 7-Zip
&lt;/span&gt;   &lt;span style=&quot;color: #5B6268;&quot;&gt;# &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;The DLLs are in tools/net9.0/any/&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;
Copy the &lt;code&gt;tools/net9.0/any/&lt;/code&gt; directory to your Emacs bin:
&lt;/p&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-shell&quot;&gt;   xcopy /E packages\csharp-ls.0.20.0\tools\net9.0\any D:\source\emacs-30.1\bin\csharp-ls&lt;span style=&quot;color: #4fb3d8;&quot;&gt;\&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;/li&gt;

&lt;li&gt;The language server is now at:
&lt;code&gt;D:\source\emacs-30.1\bin\csharp-ls\CSharpLanguageServer.dll&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org639db3a&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org639db3a&quot;&gt;Configuring Eglot for csharp-ls&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org639db3a&quot;&gt;
&lt;p&gt;
In your &lt;code&gt;init.el&lt;/code&gt;, configure Eglot to use csharp-ls:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;require&lt;/span&gt; &apos;&lt;span style=&quot;color: #ee7b29;&quot;&gt;eglot&lt;/span&gt;)&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Option A: If installed as a global tool
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; eglot-server-programs
      &apos;((csharp-mode . (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;csharp-ls&quot;&lt;/span&gt;))))&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Option B: If running from extracted DLL
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; eglot-server-programs
      &apos;((csharp-mode . (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;dotnet&quot;&lt;/span&gt; 
                        &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;D:/source/emacs-30.1/bin/csharp-ls/CSharpLanguageServer.dll&quot;&lt;/span&gt;))))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
I also have the following commented out if there are some eglot functions that causes slowdowns or I just think I don&apos;t need:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; eglot-ignored-server-capabilities
      &apos;(
        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:hoverProvider                    ; Documentation on hover
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:completionProvider               ; Code completion
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:signatureHelpProvider            ; Function signature help
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:definitionProvider               ; Go to definition
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:typeDefinitionProvider           ; Go to type definition
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:implementationProvider           ; Go to implementation
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:declarationProvider              ; Go to declaration
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:referencesProvider               ; Find references
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:documentHighlightProvider        ; Highlight symbols automatically
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:documentSymbolProvider           ; List symbols in buffer
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:workspaceSymbolProvider          ; List symbols in workspace
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:codeActionProvider               ; Execute code actions
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:codeLensProvider                 ; Code lens
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:documentFormattingProvider       ; Format buffer
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:documentRangeFormattingProvider  ; Format portion of buffer
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:documentOnTypeFormattingProvider ; On-type formatting
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:renameProvider                   ; Rename symbol
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:documentLinkProvider             ; Highlight links in document
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:colorProvider                    ; Decorate color references
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:foldingRangeProvider             ; Fold regions of buffer
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:executeCommandProvider           ; Execute custom commands
&lt;/span&gt;        &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;:inlayHintProvider                ; Inlay hints
&lt;/span&gt;        ))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org955534c&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org955534c&quot;&gt;Step 3: Setting Up the Debugger (netcoredbg)&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org955534c&quot;&gt;
&lt;p&gt;
For debugging .NET applications, we&apos;ll use &lt;code&gt;netcoredbg&lt;/code&gt;, which implements the Debug Adapter Protocol (DAP).
&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org26a7b41&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org26a7b41&quot;&gt;Installing netcoredbg&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org26a7b41&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Download from &lt;a href=&quot;https://github.com/Samsung/netcoredbg/releases&quot;&gt;Samsung&apos;s GitHub releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Extract to &lt;code&gt;D:\source\emacs-30.1\bin\netcoredbg\&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Verify: &lt;code&gt;netcoredbg.exe --version&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgd2a9278&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgd2a9278&quot;&gt;Configuring dape for Debugging&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgd2a9278&quot;&gt;
&lt;p&gt;
&lt;code&gt;dape&lt;/code&gt; is an excellent DAP client for Emacs. Here&apos;s my configuration:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; dape
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:load-path&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;z:/SharedVM/source/dape-master&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:init&lt;/span&gt;
  &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Set key prefix BEFORE loading dape
&lt;/span&gt;  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dape-key-prefix (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c d&quot;&lt;/span&gt;))
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:config&lt;/span&gt;
  &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Define common configuration
&lt;/span&gt;  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defvar&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;project-netcoredbg-path&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;d:/source/emacs-30.1/bin/netcoredbg/netcoredbg.exe&quot;&lt;/span&gt;
    &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Path to netcoredbg executable.&quot;&lt;/span&gt;)
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defvar&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;project-netcoredbg-log&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;d:/source/emacs-30.1/bin/netcoredbg/netcoredbg.log&quot;&lt;/span&gt;
    &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Path to netcoredbg log file.&quot;&lt;/span&gt;)
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defvar&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;project-project-root&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;d:/source/PROJECT&quot;&lt;/span&gt;
    &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Root directory of PROJECT project.&quot;&lt;/span&gt;)
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defvar&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;project-build-config&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Debug&quot;&lt;/span&gt;
    &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Build configuration (Debug or Release).&quot;&lt;/span&gt;)
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defvar&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;project-target-arch&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;x64&quot;&lt;/span&gt;
    &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Target architecture (x64, x86, or AnyCPU).&quot;&lt;/span&gt;)&lt;br&gt;
  &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Helper function to create component configs
&lt;/span&gt;  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;defun&lt;/span&gt; &lt;span style=&quot;color: #fedd38; font-weight: bold;&quot;&gt;project-dape-config&lt;/span&gt; (component-name dll-name &lt;span style=&quot;color: #ee7b29;&quot;&gt;&amp;amp;optional&lt;/span&gt; stop-at-entry)
    &lt;span style=&quot;color: #7bc5e1;&quot;&gt;&quot;Create a dape configuration for a component.
COMPONENT-NAME is the component directory name
DLL-NAME is the DLL filename without extension.
STOP-AT-ENTRY if non-nil, stops at program entry point.&quot;&lt;/span&gt;
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let*&lt;/span&gt; ((component-dir (format &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%s/%s&quot;&lt;/span&gt; project-project-root component-name))
           (bin-path (format &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%s/bin/%s/%s/net9.0&quot;&lt;/span&gt;
                             component-dir
                             project-target-arch
                             project-build-config))
           (dll-path (format &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%s/%s.dll&quot;&lt;/span&gt; bin-path dll-name))
           (config-name (intern (format &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;netcoredbg-launch-%s&quot;&lt;/span&gt; 
                                        (downcase component-name)))))
      `(,config-name
        modes (csharp-mode csharp-ts-mode)
        command ,project-netcoredbg-path
        command-args (,(format &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;--interpreter=vscode&quot;&lt;/span&gt;)
                      ,(format &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;--engineLogging=%s&quot;&lt;/span&gt; project-netcoredbg-log))
        normalize-path-separator &apos;windows
        &lt;span style=&quot;color: #fedd38;&quot;&gt;:type&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;coreclr&quot;&lt;/span&gt;
        &lt;span style=&quot;color: #fedd38;&quot;&gt;:request&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;launch&quot;&lt;/span&gt;
        &lt;span style=&quot;color: #fedd38;&quot;&gt;:program&lt;/span&gt; ,dll-path
        &lt;span style=&quot;color: #fedd38;&quot;&gt;:cwd&lt;/span&gt; ,component-dir
        &lt;span style=&quot;color: #fedd38;&quot;&gt;:console&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;externalTerminal&quot;&lt;/span&gt;
        &lt;span style=&quot;color: #fedd38;&quot;&gt;:internalConsoleOptions&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;neverOpen&quot;&lt;/span&gt;
        &lt;span style=&quot;color: #fedd38;&quot;&gt;:suppressJITOptimizations&lt;/span&gt; t
        &lt;span style=&quot;color: #fedd38;&quot;&gt;:requireExactSource&lt;/span&gt; nil
        &lt;span style=&quot;color: #fedd38;&quot;&gt;:justMyCode&lt;/span&gt; t
        &lt;span style=&quot;color: #fedd38;&quot;&gt;:stopAtEntry&lt;/span&gt; ,(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; stop-at-entry t &lt;span style=&quot;color: #fedd38;&quot;&gt;:json-false&lt;/span&gt;))))&lt;br&gt;
  &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Register all component configurations
&lt;/span&gt;  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;dolist&lt;/span&gt; (config (list
                   (project-dape-config &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;DM&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;DM.MSS&quot;&lt;/span&gt; t)
                   (project-dape-config &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Demo&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Demo.MSS&quot;&lt;/span&gt; t)
                   (project-dape-config &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Test_001&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Test&quot;&lt;/span&gt; t)))
    (add-to-list &apos;dape-configs config))&lt;br&gt;
  &lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Set buffer arrangement and other options
&lt;/span&gt;  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dape-buffer-window-arrangement &apos;gud)
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dape-debug t)
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; dape-repl-echo-shell-output t))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Now you can start debugging with &lt;code&gt;M-x dape&lt;/code&gt; and selecting your configuration.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgfd96775&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgfd96775&quot;&gt;Step 4: Installing Supporting Tools&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgfd96775&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orge7d67eb&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orge7d67eb&quot;&gt;Portable Git&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orge7d67eb&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Download &lt;code&gt;PortableGit-2.50.0-64-bit.7z.exe&lt;/code&gt; from &lt;a href=&quot;https://git-scm.com/download/win&quot;&gt;git-scm.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Run and extract to &lt;code&gt;D:\source\emacs-30.1\bin\PortableGit\&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
This gives you &lt;code&gt;git.exe&lt;/code&gt;, &lt;code&gt;bash.exe&lt;/code&gt;, and a whole Unix-like environment.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org245ac14&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org245ac14&quot;&gt;ripgrep (Fast Searching)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org245ac14&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Download from &lt;a href=&quot;https://github.com/BurntSushi/ripgrep/releases&quot;&gt;ripgrep releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Extract &lt;code&gt;rg.exe&lt;/code&gt; to &lt;code&gt;D:\source\emacs-30.1\bin\find\&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
ripgrep is dramatically faster than grep for searching codebases.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orge45f730&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orge45f730&quot;&gt;Hunspell (Spell Checking)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orge45f730&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Download &lt;code&gt;hunspell-1.3.2-3-w32-bin.zip&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Extract to &lt;code&gt;D:\source\emacs-30.1\bin\hunspell\&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Download dictionary files (&lt;code&gt;en_GB.dic&lt;/code&gt; and &lt;code&gt;en_GB.aff&lt;/code&gt;) and place in &lt;code&gt;hunspell\share\hunspell\&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org54bac8b&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org54bac8b&quot;&gt;ImageMagick (Image Processing)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org54bac8b&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Download the portable Q16 x64 version from &lt;a href=&quot;https://imagemagick.org/script/download.php&quot;&gt;imagemagick.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Extract to &lt;code&gt;D:\source\emacs-30.1\bin\ImageMagick-7.1.2-9-portable-Q16-x64\&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
This enables &lt;code&gt;image-dired&lt;/code&gt; thumbnail generation.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org6e681b9&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org6e681b9&quot;&gt;FFmpeg (Video Processing)&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org6e681b9&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Download from &lt;a href=&quot;https://ffmpeg.org/download.html&quot;&gt;ffmpeg.org&lt;/a&gt; (essentials build is fine)&lt;/li&gt;
&lt;li&gt;Extract to &lt;code&gt;D:\source\emacs-30.1\bin\ffmpeg-7.1.1-essentials_build\&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;
Useful for video thumbnails in dired and media processing.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org1033462&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org1033462&quot;&gt;Step 5: Configuring the PATH&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org1033462&quot;&gt;
&lt;p&gt;
This is crucial-Emacs needs to find all these tools. Here&apos;s the PATH configuration from my &lt;code&gt;init.el&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;when&lt;/span&gt; (eq system-type &apos;windows-nt)
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;let*&lt;/span&gt; ((emacs-bin &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;d:/source/emacs-30.1/bin&quot;&lt;/span&gt;)
         (xPaths
          `(,emacs-bin
            ,(concat emacs-bin &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/PortableGit/bin&quot;&lt;/span&gt;)
            ,(concat emacs-bin &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/PortableGit/usr/bin&quot;&lt;/span&gt;)
            ,(concat emacs-bin &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/hunspell/bin&quot;&lt;/span&gt;)
            ,(concat emacs-bin &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/find&quot;&lt;/span&gt;)
            ,(concat emacs-bin &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/netcoredbg&quot;&lt;/span&gt;)
            ,(concat emacs-bin &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/csharp-ls/tools/net9.0/any&quot;&lt;/span&gt;)
            ,(concat emacs-bin &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/ffmpeg-7.1.1-essentials_build/bin&quot;&lt;/span&gt;)
            ,(concat emacs-bin &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/ImageMagick-7.1.2-9-portable-Q16-x64&quot;&lt;/span&gt;)))
         (winPaths (getenv &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;PATH&quot;&lt;/span&gt;)))
    (setenv &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;PATH&quot;&lt;/span&gt; (concat (mapconcat &apos;identity xPaths &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;;&quot;&lt;/span&gt;) &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;;&quot;&lt;/span&gt; winPaths))
    (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; exec-path (append xPaths (parse-colon-path winPaths)))))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org911db39&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org911db39&quot;&gt;Step 6: Installing Emacs Packages&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org911db39&quot;&gt;
&lt;p&gt;
Extract these to a shared location or download from MELPA
&lt;/p&gt;

&lt;table class=&quot;table table-striped&quot; border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;


&lt;colgroup&gt;
&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Package&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;corfu&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Modern completion UI&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;dape&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Debug Adapter Protocol client&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;highlight-indent-guides&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Visual indentation guides&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;ztree&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Directory tree comparison&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;web-mode&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Web template editing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;
Example package configuration:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; corfu
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:load-path&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;z:/SharedVM/source/corfu-main&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:custom&lt;/span&gt;
  (corfu-auto nil)         &lt;span style=&quot;color: #5B6268;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Manual completion trigger
&lt;/span&gt;  (corfu-cycle t)          &lt;span style=&quot;color: #5B6268;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Cycle through candidates
&lt;/span&gt;  (corfu-preselect &apos;first))&lt;br&gt;
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; ztree
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:load-path&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;z:/SharedVM/source/ztree&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:config&lt;/span&gt;
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; ztree-diff-filter-list
        &apos;(&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;build&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;\\.dll&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;\\.git&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;bin&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;obj&quot;&lt;/span&gt;))
  (global-set-key (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c z d&quot;&lt;/span&gt;) &apos;ztree-diff))&lt;br&gt;
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; web-mode
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:load-path&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;z:/SharedVM/source/web-mode-master&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:mode&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;\\.cshtml?\\&apos;&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:hook&lt;/span&gt; (html-mode . web-mode)
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:bind&lt;/span&gt; (&lt;span style=&quot;color: #fedd38;&quot;&gt;:map&lt;/span&gt; web-mode-map (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;M-;&quot;&lt;/span&gt; . nil)))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Note that I turn off autocomplete for corfu and complete using &lt;code&gt;complete-symbol&lt;/code&gt; manually, otherwise the LSP is constantly accessed with slowdown.
&lt;/p&gt;

&lt;p&gt;
I often use &lt;code&gt;Meld&lt;/code&gt; but am currently am looking to adapt &lt;code&gt;ztree&lt;/code&gt; to perform better for directory comparisons.
&lt;/p&gt;

&lt;p&gt;
Web-mode is the best package I have found for html type file navigation and folding, very useful when developing Razor pages for example.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgdd9e2d2&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgdd9e2d2&quot;&gt;Step 7: auto open file modes&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgdd9e2d2&quot;&gt;
&lt;p&gt;
Of course running and building in windows means in Emacs probably having to open .csproj files from time to time, well &lt;code&gt;nxml-mode&lt;/code&gt; comes in useful for this:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(add-to-list &apos;auto-mode-alist &apos;(&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;\\.csproj\\&apos;&quot;&lt;/span&gt; . nxml-mode))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org8edb9d9&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org8edb9d9&quot;&gt;Step 8: build script&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org8edb9d9&quot;&gt;
&lt;p&gt;
Here is my general build script, leveraging msbuild and running generally from &lt;code&gt;eshell&lt;/code&gt;
&lt;/p&gt;

&lt;p&gt;
New projects are added to :
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bat&quot;&gt;&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECTS&lt;/span&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECT_NAMES&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bat&quot;&gt;@&lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; off
&lt;span style=&quot;color: #fedd38;&quot;&gt;setlocal&lt;/span&gt;&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;=================================================================
&lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Build Management Script
&lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;=================================================================
&lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Usage: build-selected.bat [action] [verbosity] [configuration] [platform]
&lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;REM   &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;action: build, clean, restore, rebuild (default: build)
&lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;REM   &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;verbosity: quiet, minimal, normal, detailed, diagnostic (default: minimal)
&lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;REM   &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;configuration: Debug, Release (default: Debug)
&lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;REM   &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;platform: x64, x86, &quot;Any CPU&quot; (default: x64)
&lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;=================================================================
&lt;/span&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Set defaults
&lt;/span&gt;&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;ACTION&lt;/span&gt;=%&lt;span style=&quot;color: #cbccd1;&quot;&gt;1&lt;/span&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;VERBOSITY&lt;/span&gt;=%&lt;span style=&quot;color: #cbccd1;&quot;&gt;2&lt;/span&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;CONFIGURATION&lt;/span&gt;=%&lt;span style=&quot;color: #cbccd1;&quot;&gt;3&lt;/span&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PLATFORM&lt;/span&gt;=%&lt;span style=&quot;color: #cbccd1;&quot;&gt;4&lt;/span&gt;&lt;br&gt;
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%ACTION%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;ACTION&lt;/span&gt;=build
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%VERBOSITY%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;VERBOSITY&lt;/span&gt;=minimal
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%CONFIGURATION%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;CONFIGURATION&lt;/span&gt;=Debug
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%PLATFORM%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PLATFORM&lt;/span&gt;=x64&lt;br&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; Build Script - Action=%&lt;span style=&quot;color: #cbccd1;&quot;&gt;ACTION&lt;/span&gt;%, Verbosity=%&lt;span style=&quot;color: #cbccd1;&quot;&gt;VERBOSITY&lt;/span&gt;%, Config=%&lt;span style=&quot;color: #cbccd1;&quot;&gt;CONFIGURATION&lt;/span&gt;%, Platform=%&lt;span style=&quot;color: #cbccd1;&quot;&gt;PLATFORM&lt;/span&gt;%
&lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt;.&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Common build parameters
&lt;/span&gt;&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;BUILD_PARAMS&lt;/span&gt;=/&lt;span style=&quot;color: #ee7b29;&quot;&gt;p&lt;/span&gt;:Configuration=%&lt;span style=&quot;color: #cbccd1;&quot;&gt;CONFIGURATION&lt;/span&gt;% /&lt;span style=&quot;color: #ee7b29;&quot;&gt;p&lt;/span&gt;:Platform=&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%PLATFORM%&quot;&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;verbosity&lt;/span&gt;:%&lt;span style=&quot;color: #cbccd1;&quot;&gt;VERBOSITY&lt;/span&gt;%&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Set MSBuild target based on action
&lt;/span&gt;&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;I&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%ACTION%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;build&quot;&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;TARGET&lt;/span&gt;=Build
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;I&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%ACTION%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;clean&quot;&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;TARGET&lt;/span&gt;=Clean
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;I&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%ACTION%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;restore&quot;&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;TARGET&lt;/span&gt;=Restore
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;I&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%ACTION%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;rebuild&quot;&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;TARGET&lt;/span&gt;=Rebuild&lt;br&gt;
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%TARGET%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt; (
    &lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; Error: Invalid action &apos;%&lt;span style=&quot;color: #cbccd1;&quot;&gt;ACTION&lt;/span&gt;%&apos;. Use: build, clean, restore, or rebuild
    &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;exit&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;b&lt;/span&gt; 1
)&lt;br&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; Executing %&lt;span style=&quot;color: #cbccd1;&quot;&gt;ACTION&lt;/span&gt;% action...
&lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt;.&lt;br&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECTS&lt;/span&gt;[1]=Demo/Demo.csproj
&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECT_NAMES&lt;/span&gt;[1]=Demo&lt;br&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECTS&lt;/span&gt;[2]=Test/Test.csproj
&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECT_NAMES&lt;/span&gt;[2]=Test&lt;br&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECT_COUNT&lt;/span&gt;=2&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Special handling for rebuild (clean then build)
&lt;/span&gt;&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;I&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%ACTION%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;rebuild&quot;&lt;/span&gt; (
    &lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; === CLEANING PHASE ===
    &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;for&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;L&lt;/span&gt; %%&lt;span style=&quot;color: #cbccd1;&quot;&gt;i&lt;/span&gt; &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;in&lt;/span&gt; (1,1,%&lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECT_COUNT&lt;/span&gt;%) &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;do&lt;/span&gt; (
        &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;call&lt;/span&gt; &lt;span style=&quot;color: #ee7b29;&quot;&gt;:process_project&lt;/span&gt; %%&lt;span style=&quot;color: #cbccd1;&quot;&gt;i&lt;/span&gt; Clean
        &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; errorlevel 1 &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;goto&lt;/span&gt; &lt;span style=&quot;color: #ee7b29;&quot;&gt;:error&lt;/span&gt;
    )
    &lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt;.
    &lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; === BUILDING PHASE ===
    &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;TARGET&lt;/span&gt;=Build
)&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Process all active projects
&lt;/span&gt;&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;for&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;L&lt;/span&gt; %%&lt;span style=&quot;color: #cbccd1;&quot;&gt;i&lt;/span&gt; &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;in&lt;/span&gt; (1,1,%&lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECT_COUNT&lt;/span&gt;%) &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;do&lt;/span&gt; (
    &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;call&lt;/span&gt; &lt;span style=&quot;color: #ee7b29;&quot;&gt;:process_project&lt;/span&gt; %%&lt;span style=&quot;color: #cbccd1;&quot;&gt;i&lt;/span&gt; %&lt;span style=&quot;color: #cbccd1;&quot;&gt;TARGET&lt;/span&gt;%
    &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; errorlevel 1 &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;goto&lt;/span&gt; &lt;span style=&quot;color: #ee7b29;&quot;&gt;:error&lt;/span&gt;
)&lt;br&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt;.
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;I&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%ACTION%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;clean&quot;&lt;/span&gt; (
    &lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; All selected components cleaned successfully!
) &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;else&lt;/span&gt; &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;I&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%ACTION%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;restore&quot;&lt;/span&gt; (
    &lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; All selected components restored successfully!
) &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;else&lt;/span&gt; &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;I&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;%ACTION%&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;rebuild&quot;&lt;/span&gt; (
    &lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; All selected components rebuilt successfully!
) &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;else&lt;/span&gt; (
    &lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; All selected components built successfully!
)
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;goto&lt;/span&gt; &lt;span style=&quot;color: #ee7b29;&quot;&gt;:end&lt;/span&gt;&lt;br&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;:process_project&lt;/span&gt;
    &lt;span style=&quot;color: #fedd38;&quot;&gt;setlocal&lt;/span&gt; EnableDelayedExpansion
    &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;idx&lt;/span&gt;=%&lt;span style=&quot;color: #cbccd1;&quot;&gt;1&lt;/span&gt;
    &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;target&lt;/span&gt;=%&lt;span style=&quot;color: #cbccd1;&quot;&gt;2&lt;/span&gt;&lt;br&gt;
    &lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Get project path and name using the index
&lt;/span&gt;    &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;for&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;f&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;tokens=2 delims==&quot;&lt;/span&gt; %%&lt;span style=&quot;color: #cbccd1;&quot;&gt;a&lt;/span&gt; &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;in&lt;/span&gt; (&apos;&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECTS&lt;/span&gt;[%&lt;span style=&quot;color: #cbccd1;&quot;&gt;idx&lt;/span&gt;%] 2^&amp;gt;nul&apos;) &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;do&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECT_PATH&lt;/span&gt;=%%&lt;span style=&quot;color: #cbccd1;&quot;&gt;a&lt;/span&gt;
    &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;for&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;f&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;tokens=2 delims==&quot;&lt;/span&gt; %%&lt;span style=&quot;color: #cbccd1;&quot;&gt;a&lt;/span&gt; &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;in&lt;/span&gt; (&apos;&lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECT_NAMES&lt;/span&gt;[%&lt;span style=&quot;color: #cbccd1;&quot;&gt;idx&lt;/span&gt;%] 2^&amp;gt;nul&apos;) &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;do&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;set&lt;/span&gt; &lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECT_NAME&lt;/span&gt;=%%&lt;span style=&quot;color: #cbccd1;&quot;&gt;a&lt;/span&gt;&lt;br&gt;
    &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;!PROJECT_PATH!&quot;&lt;/span&gt;==&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;goto&lt;/span&gt; &lt;span style=&quot;color: #ee7b29;&quot;&gt;:eof&lt;/span&gt;&lt;br&gt;
    &lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; ----------------------------------------
    &lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; [%&lt;span style=&quot;color: #cbccd1;&quot;&gt;idx&lt;/span&gt;%/%&lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECT_COUNT&lt;/span&gt;%] %&lt;span style=&quot;color: #cbccd1;&quot;&gt;target&lt;/span&gt;%ing !&lt;span style=&quot;color: #cbccd1;&quot;&gt;PROJECT_NAME&lt;/span&gt;!...&lt;br&gt;
    &lt;span style=&quot;color: #5B6268;&quot;&gt;REM &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Build the project normally
&lt;/span&gt;    msbuild &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;!PROJECT_PATH!&quot;&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;t&lt;/span&gt;:%&lt;span style=&quot;color: #cbccd1;&quot;&gt;target&lt;/span&gt;% %&lt;span style=&quot;color: #cbccd1;&quot;&gt;BUILD_PARAMS&lt;/span&gt;%
    &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;if&lt;/span&gt; errorlevel 1 &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;exit&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;b&lt;/span&gt; 1&lt;br&gt;
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;goto&lt;/span&gt; &lt;span style=&quot;color: #ee7b29;&quot;&gt;:eof&lt;/span&gt;&lt;br&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;:error&lt;/span&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt;.
&lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; %&lt;span style=&quot;color: #cbccd1;&quot;&gt;ACTION&lt;/span&gt;% failed! Check the output above &lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;for&lt;/span&gt; errors.
&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;exit&lt;/span&gt; /&lt;span style=&quot;color: #ee7b29;&quot;&gt;b&lt;/span&gt; 1&lt;br&gt;
&lt;span style=&quot;font-weight: bold;&quot;&gt;:end&lt;/span&gt;
&lt;span style=&quot;color: #fedd38;&quot;&gt;echo&lt;/span&gt; %&lt;span style=&quot;color: #cbccd1;&quot;&gt;ACTION&lt;/span&gt;% completed &lt;span style=&quot;color: #fedd38;&quot;&gt;at&lt;/span&gt; %&lt;span style=&quot;color: #cbccd1;&quot;&gt;time&lt;/span&gt;%
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
to launch applications of course, if it is a pure DOTNET project you would use &lt;code&gt;dotnet run&lt;/code&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgc8c1c5d&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgc8c1c5d&quot;&gt;Troubleshooting&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgc8c1c5d&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgae71c79&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgae71c79&quot;&gt;&quot;Cannot find csharp-ls&quot; or Eglot won&apos;t start&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgae71c79&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Check the PATH: &lt;code&gt;M-x getenv RET PATH&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Verify the DLL exists at the configured location&lt;/li&gt;
&lt;li&gt;Try running manually: &lt;code&gt;dotnet path\to\CSharpLanguageServer.dll --version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;*eglot-events*&lt;/code&gt; buffer for detailed error messages&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orga512d27&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orga512d27&quot;&gt;LSP is slow or uses too much memory&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orga512d27&quot;&gt;
&lt;p&gt;
Try adding to your configuration:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Increase garbage collection threshold during LSP operations
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; gc-cons-threshold 100000000)  &lt;span style=&quot;color: #5B6268;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;100MB
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; read-process-output-max (* 1024 1024))  &lt;span style=&quot;color: #5B6268;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;1MB&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgc4a1901&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgc4a1901&quot;&gt;Debugger won&apos;t attach&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgc4a1901&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Ensure the project is built in Debug configuration&lt;/li&gt;
&lt;li&gt;Check the DLL path matches your build output&lt;/li&gt;
&lt;li&gt;Look at &lt;code&gt;*dape-repl*&lt;/code&gt; for error messages&lt;/li&gt;
&lt;li&gt;Verify netcoredbg runs: &lt;code&gt;netcoredbg.exe --version&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgafc5e3f&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgafc5e3f&quot;&gt;Conclusion&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgafc5e3f&quot;&gt;
&lt;p&gt;
This setup has served me well for my windows .NET 9.0 projects and various other C# work. The key benefits:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;Portability&lt;/b&gt;: Everything lives in one directory&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Speed&lt;/b&gt;: csharp-ls is notably faster than OmniSharp&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Flexibility&lt;/b&gt;: Easy to customise and extend&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Offline-capable&lt;/b&gt;: Works in air-gapped environments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
The initial setup takes some effort, but once it&apos;s done, you have a powerful, consistent development environment that travels with you.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
</content>
</entry>
<entry>
<title>Expanding Ollama Buddy: Mistral Codestral Integration</title>
<link href="https://emacs.dyerdwelling.family/emacs/20251211081935-emacs--expanding-ollama-buddy-mistral-codestral-integration/"/>
<id>https://emacs.dyerdwelling.family/emacs/20251211081935-emacs--expanding-ollama-buddy-mistral-codestral-integration/</id>
<updated>2025-12-11T08:19:00+0000</updated>
<content type="html">&lt;p&gt;
Ollama Buddy now supports Mistral&apos;s Codestral - a powerful code-generation model from Mistral AI that seamlessly integrates into the ollama-buddy ecosystem.
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://github.com/captainflasmr/ollama-buddy&quot;&gt;https://github.com/captainflasmr/ollama-buddy&lt;/a&gt;
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://melpa.org/#/ollama-buddy&quot;&gt;https://melpa.org/#/ollama-buddy&lt;/a&gt;
&lt;/p&gt;


&lt;div id=&quot;orgc498761&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions_001.jpg&quot; alt=&quot;20250424085731-emacs--Ollama-Buddy-0-9-35-Grok-Gemini-Integration-Enhanced-Sessions_001.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
So now we have:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;b&gt;Local Ollama models&lt;/b&gt; - full control, complete privacy&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OpenAI&lt;/b&gt; - extensive model options and API maturity&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Claude&lt;/b&gt; - reasoning and complex analysis&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Gemini&lt;/b&gt; - multimodal capabilities&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Grok&lt;/b&gt; - advanced reasoning models&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Codestral&lt;/b&gt; - specialized code generation &lt;b&gt;NEW&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;
To get up and running&amp;#x2026;
&lt;/p&gt;

&lt;p&gt;
First, sign up at &lt;a href=&quot;https://console.mistral.ai/&quot;&gt;Mistral AI&lt;/a&gt; and generate an API key from your dashboard.
&lt;/p&gt;

&lt;p&gt;
Add this to your Emacs configuration:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-elisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; ollama-buddy
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:bind&lt;/span&gt;
  (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c o&quot;&lt;/span&gt; . ollama-buddy-menu)
  (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c O&quot;&lt;/span&gt; . ollama-buddy-transient-menu-wrapper)
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:custom&lt;/span&gt;
  (ollama-buddy-codestral-api-key
   (auth-source-pick-first-password &lt;span style=&quot;color: #fedd38;&quot;&gt;:host&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;ollama-buddy-codestral&quot;&lt;/span&gt; &lt;span style=&quot;color: #fedd38;&quot;&gt;:user&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;apikey&quot;&lt;/span&gt;))
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:config&lt;/span&gt;
  (&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;require&lt;/span&gt; &apos;&lt;span style=&quot;color: #ee7b29;&quot;&gt;ollama-buddy-codestral&lt;/span&gt; nil t))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Once configured, Codestral models will appear in your model list with an &lt;code&gt;s:&lt;/code&gt; prefix (e.g., &lt;code&gt;s:codestral-latest&lt;/code&gt;). You can:
&lt;/p&gt;

&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Select it from the model menu (&lt;code&gt;C-c m&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Use it with any command that supports model selection&lt;/li&gt;
&lt;li&gt;Switch between local and cloud models on-the-fly&lt;/li&gt;
&lt;/ul&gt;
</content>
</entry>
<entry>
<title>Convert copied jira kanban to org (jira-to-org)</title>
<link href="https://emacs.dyerdwelling.family/emacs/20251128145610-emacs--jira-to-org/"/>
<id>https://emacs.dyerdwelling.family/emacs/20251128145610-emacs--jira-to-org/</id>
<updated>2025-11-28T14:56:00+0000</updated>
<content type="html">&lt;p&gt;
I have been fiddling around with some very rudimentary Jira integration to org, basically something very simple, just a regular copy from the kanban and then convert into org headlines!
&lt;/p&gt;

&lt;p&gt;
This package is designed for simpler workflows where you copy data from Jira rather than maintaining API integration.
&lt;/p&gt;


&lt;div id=&quot;org1dad147&quot; class=&quot;figure&quot;&gt;
&lt;p&gt;&lt;img class=&quot;img-fluid rounded&quot; src=&quot;/static/emacs/20251128145610-emacs--jira-to-org.jpg&quot; alt=&quot;20251128145610-emacs--jira-to-org.jpg&quot; width=&quot;100%&quot; /&gt;
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
Here is a link to the package:
&lt;/p&gt;

&lt;p&gt;
&lt;a href=&quot;https://github.com/captainflasmr/jira-to-org&quot;&gt;https://github.com/captainflasmr/jira-to-org&lt;/a&gt;
&lt;/p&gt;
&lt;section id=&quot;outline-container-org1a35aad&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org1a35aad&quot;&gt;Overview&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org1a35aad&quot;&gt;
&lt;p&gt;
&lt;code&gt;jira-to-org&lt;/code&gt; is an Emacs package that converts Jira sprint board data into org-mode task entries. It parses text copied directly from Jira sprint boards and transforms it into properly formatted org-mode headings with TODO keywords, priorities, tags, and metadata.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org458e4d6&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org458e4d6&quot;&gt;Features&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org458e4d6&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Parse Jira sprint data from buffers, regions, or clipboard&lt;/li&gt;
&lt;li&gt;Convert Jira statuses to org-mode TODO keywords&lt;/li&gt;
&lt;li&gt;Map Jira priorities to org-mode priorities (A/B/C)&lt;/li&gt;
&lt;li&gt;Generate tags from assignees, sprint identifiers, and years&lt;/li&gt;
&lt;li&gt;Update existing org entries based on fresh Jira data&lt;/li&gt;
&lt;li&gt;Customizable mappings for assignees, priorities, and statuses&lt;/li&gt;
&lt;li&gt;Configurable heading levels and default values&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org6818c8a&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org6818c8a&quot;&gt;Installation&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org6818c8a&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgb6fb282&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgb6fb282&quot;&gt;Manual Installation&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgb6fb282&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Download &lt;code&gt;jira-to-org.el&lt;/code&gt; to your Emacs configuration directory&lt;/li&gt;
&lt;li&gt;Add to your &lt;code&gt;init.el&lt;/code&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(add-to-list &apos;load-path &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/path/to/jira-to-org/&quot;&lt;/span&gt;)
(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;require&lt;/span&gt; &apos;&lt;span style=&quot;color: #ee7b29;&quot;&gt;jira-to-org&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org074ec68&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org074ec68&quot;&gt;Using &lt;code&gt;use-package&lt;/code&gt;&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org074ec68&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; jira-to-org
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:load-path&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;/path/to/jira-to-org/&quot;&lt;/span&gt;
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:custom&lt;/span&gt;
  (jira-to-org-default-sprint &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;s7&quot;&lt;/span&gt;)
  (jira-to-org-default-year &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;2025&quot;&lt;/span&gt;)
  (jira-to-org-heading-level 4))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org594f4f6&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org594f4f6&quot;&gt;Usage&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org594f4f6&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org4a136de&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org4a136de&quot;&gt;Basic Workflow&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org4a136de&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Open your Jira sprint board in a web browser&lt;/li&gt;
&lt;li&gt;Select and copy the sprint data (the entire board or specific columns)&lt;/li&gt;
&lt;li&gt;In Emacs, use one of the parsing commands&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgfc9c086&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgfc9c086&quot;&gt;Commands&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgfc9c086&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org67d9d15&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;org67d9d15&quot;&gt;&lt;code&gt;jira-to-org-parse-buffer&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-org67d9d15&quot;&gt;
&lt;p&gt;
Parse the entire current buffer as Jira data. The result is copied to the kill ring.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;M-x jira-to-org-parse-buffer
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
With prefix argument (&lt;code&gt;C-u&lt;/code&gt;), prompts for a sprint tag:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;C-u M-x jira-to-org-parse-buffer
Sprint tag (e.g., s7): s8
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org89270ec&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;org89270ec&quot;&gt;&lt;code&gt;jira-to-org-parse-region&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-org89270ec&quot;&gt;
&lt;p&gt;
Parse Jira data in the selected region. The result is copied to the kill ring.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;1. Select region containing Jira data
2. M-x jira-to-org-parse-region
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org03bc04d&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;org03bc04d&quot;&gt;&lt;code&gt;jira-to-org-parse-and-insert&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-org03bc04d&quot;&gt;
&lt;p&gt;
Parse Jira data from the clipboard and insert the org headings at point.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;1. Copy Jira data to clipboard
2. Position cursor where you want entries inserted
3. M-x jira-to-org-parse-and-insert
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgd01d8b5&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;orgd01d8b5&quot;&gt;&lt;code&gt;jira-to-org-update-from-jira&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-orgd01d8b5&quot;&gt;
&lt;p&gt;
Update existing org entries based on fresh Jira data. This command:
&lt;/p&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Updates TODO statuses for entries that already exist in your buffer&lt;/li&gt;
&lt;li&gt;Reports how many entries were updated&lt;/li&gt;
&lt;li&gt;Copies new items (not found in buffer) to the kill ring&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-bash&quot;&gt;M-x jira-to-org-update-from-jira
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org22a0283&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org22a0283&quot;&gt;Example Input Format&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org22a0283&quot;&gt;
&lt;p&gt;
The package expects text copied from Jira sprint boards in this format:
&lt;/p&gt;

&lt;pre class=&quot;example&quot; id=&quot;orgceade1e&quot;&gt;
To Do
MM-78
Implement IG connection management APIs
Assignee: James Dyer
Priority: Medium
Issue Type: Story
2

In Progress
MM-79
Create gRPC service definition
Assignee: Freddy
Priority: High
3

Done
MM-75
Setup project structure
Assignee: N Cropper
Priority: Low
Issue Type: Task
1
&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org4ae863c&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org4ae863c&quot;&gt;Example Output&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org4ae863c&quot;&gt;
&lt;p&gt;
The above input would be converted to:
&lt;/p&gt;

&lt;p&gt;
#+BEGIN_EXAMPLE
&lt;/p&gt;
&lt;/div&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;a id=&quot;org5ef4621&quot;&gt;&lt;/a&gt;&lt;span class=&quot;todo TODO&quot;&gt;TODO&lt;/span&gt; MM-78 Implement IG connection management APIs&amp;#xa0;&amp;#xa0;&amp;#xa0;&lt;span class=&quot;tag&quot;&gt;&lt;span class=&quot;s7&quot;&gt;s7&lt;/span&gt;&amp;#xa0;&lt;span class=&quot;jdyer&quot;&gt;jdyer&lt;/span&gt;&amp;#xa0;&lt;span class=&quot;2025&quot;&gt;2025&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;a id=&quot;orgf7d417a&quot;&gt;&lt;/a&gt;&lt;span class=&quot;todo DOING&quot;&gt;DOING&lt;/span&gt; MM-79 Create gRPC service definition&amp;#xa0;&amp;#xa0;&amp;#xa0;&lt;span class=&quot;tag&quot;&gt;&lt;span class=&quot;s7&quot;&gt;s7&lt;/span&gt;&amp;#xa0;&lt;span class=&quot;freddy&quot;&gt;freddy&lt;/span&gt;&amp;#xa0;&lt;span class=&quot;2025&quot;&gt;2025&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;
&lt;li&gt;&lt;a id=&quot;org586cc0a&quot;&gt;&lt;/a&gt;&lt;span class=&quot;done DONE&quot;&gt;DONE&lt;/span&gt; MM-75 Setup project structure&amp;#xa0;&amp;#xa0;&amp;#xa0;&lt;span class=&quot;tag&quot;&gt;&lt;span class=&quot;s7&quot;&gt;s7&lt;/span&gt;&amp;#xa0;&lt;span class=&quot;ncropper&quot;&gt;ncropper&lt;/span&gt;&amp;#xa0;&lt;span class=&quot;2025&quot;&gt;2025&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div class=&quot;outline-text-5&quot; id=&quot;text-org586cc0a&quot;&gt;
&lt;p&gt;
#+END_EXAMPLE
&lt;/p&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org1417b59&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org1417b59&quot;&gt;Configuration&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org1417b59&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org5216721&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org5216721&quot;&gt;Customization Variables&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org5216721&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orge838352&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;orge838352&quot;&gt;&lt;code&gt;jira-to-org-default-sprint&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-orge838352&quot;&gt;
&lt;p&gt;
Default sprint tag to add to all entries (e.g., &lt;code&gt;&quot;s7&quot;&lt;/code&gt;). If &lt;code&gt;nil&lt;/code&gt;, no sprint tag is added.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; jira-to-org-default-sprint &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;s7&quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgdc93c8a&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;orgdc93c8a&quot;&gt;&lt;code&gt;jira-to-org-default-year&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-orgdc93c8a&quot;&gt;
&lt;p&gt;
Default year to add as a tag to all entries.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; jira-to-org-default-year &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;2025&quot;&lt;/span&gt;)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org6f436d9&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;org6f436d9&quot;&gt;&lt;code&gt;jira-to-org-assignee-map&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-org6f436d9&quot;&gt;
&lt;p&gt;
Alist mapping full names (as they appear in Jira) to short tag names.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; jira-to-org-assignee-map
      &apos;((&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;James Dyer&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;jdyer&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Freddy&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;freddy&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;N Cropper&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;ncropper&quot;&lt;/span&gt;)))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org615951a&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;org615951a&quot;&gt;&lt;code&gt;jira-to-org-priority-map&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-org615951a&quot;&gt;
&lt;p&gt;
Alist mapping Jira priority levels to org-mode priority letters.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; jira-to-org-priority-map
      &apos;((&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Highest&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;A&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;High&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;A&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Medium&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;B&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Low&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Lowest&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C&quot;&lt;/span&gt;)))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org0072453&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;org0072453&quot;&gt;&lt;code&gt;jira-to-org-status-map&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-org0072453&quot;&gt;
&lt;p&gt;
Alist mapping Jira status names to org-mode TODO keywords.
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; jira-to-org-status-map
      &apos;((&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;To Do&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;TODO&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;In Progress&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;DOING&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Done&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;DONE&quot;&lt;/span&gt;)))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org1769c25&quot; class=&quot;outline-4&quot;&gt;
&lt;h4 id=&quot;org1769c25&quot;&gt;&lt;code&gt;jira-to-org-heading-level&lt;/code&gt;&lt;/h4&gt;
&lt;div class=&quot;outline-text-4&quot; id=&quot;text-org1769c25&quot;&gt;
&lt;p&gt;
Number of asterisks for generated org headings (default: 4).
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; jira-to-org-heading-level 3)  &lt;span style=&quot;color: #5B6268;&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Use *** instead of ****&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org18d5e51&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org18d5e51&quot;&gt;Complete Configuration Example&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org18d5e51&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;use-package&lt;/span&gt; jira-to-org
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:custom&lt;/span&gt;
  (jira-to-org-default-sprint &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;s8&quot;&lt;/span&gt;)
  (jira-to-org-default-year &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;2025&quot;&lt;/span&gt;)
  (jira-to-org-heading-level 4)
  (jira-to-org-assignee-map
   &apos;((&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;James Dyer&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;jdyer&quot;&lt;/span&gt;)
     (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Freddy Johnson&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;freddy&quot;&lt;/span&gt;)
     (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Sarah Chen&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;schen&quot;&lt;/span&gt;)
     (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Mike Wilson&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;mwilson&quot;&lt;/span&gt;)))
  (jira-to-org-priority-map
   &apos;((&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Highest&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;A&quot;&lt;/span&gt;)
     (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;High&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;A&quot;&lt;/span&gt;)
     (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Medium&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;B&quot;&lt;/span&gt;)
     (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Low&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C&quot;&lt;/span&gt;)
     (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Lowest&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C&quot;&lt;/span&gt;)))
  (jira-to-org-status-map
   &apos;((&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;To Do&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;TODO&quot;&lt;/span&gt;)
     (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;In Progress&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;DOING&quot;&lt;/span&gt;)
     (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;In Review&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;REVIEW&quot;&lt;/span&gt;)
     (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Done&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;DONE&quot;&lt;/span&gt;)))
  &lt;span style=&quot;color: #fedd38;&quot;&gt;:bind&lt;/span&gt;
  (&lt;span style=&quot;color: #fedd38;&quot;&gt;:map&lt;/span&gt; org-mode-map
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c j p&quot;&lt;/span&gt; . jira-to-org-parse-and-insert)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c j u&quot;&lt;/span&gt; . jira-to-org-update-from-jira)))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgb55578f&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgb55578f&quot;&gt;Workflow Integration&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgb55578f&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org501f05e&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org501f05e&quot;&gt;Sprint Planning Workflow&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org501f05e&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Open your sprint planning org file&lt;/li&gt;
&lt;li&gt;Navigate to the sprint section&lt;/li&gt;
&lt;li&gt;Copy the Jira sprint board data&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;M-x jira-to-org-parse-and-insert&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Adjust any entries as needed&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgb5bf058&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgb5bf058&quot;&gt;Daily Standup Updates&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgb5bf058&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Copy current sprint board state from Jira&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;M-x jira-to-org-update-from-jira&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Review updated TODO states&lt;/li&gt;
&lt;li&gt;Any new items are copied to kill ring for manual placement&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgcf2080f&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgcf2080f&quot;&gt;Custom Key Bindings&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgcf2080f&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Add to your org-mode configuration
&lt;/span&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-eval-after-load&lt;/span&gt; &apos;org
  (define-key org-mode-map (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c j p&quot;&lt;/span&gt;) #&apos;jira-to-org-parse-and-insert)
  (define-key org-mode-map (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c j r&quot;&lt;/span&gt;) #&apos;jira-to-org-parse-region)
  (define-key org-mode-map (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c j b&quot;&lt;/span&gt;) #&apos;jira-to-org-parse-buffer)
  (define-key org-mode-map (kbd &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;C-c j u&quot;&lt;/span&gt;) #&apos;jira-to-org-update-from-jira))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org8eade85&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org8eade85&quot;&gt;Parsed Fields&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org8eade85&quot;&gt;
&lt;p&gt;
The package extracts and uses the following fields from Jira data:
&lt;/p&gt;

&lt;table class=&quot;table table-striped&quot; border=&quot;2&quot; cellspacing=&quot;0&quot; cellpadding=&quot;6&quot; rules=&quot;groups&quot; frame=&quot;hsides&quot;&gt;


&lt;colgroup&gt;
&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;

&lt;col  class=&quot;org-left&quot; /&gt;
&lt;/colgroup&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Jira Field&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Usage&lt;/th&gt;
&lt;th scope=&quot;col&quot; class=&quot;org-left&quot;&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;Issue Key&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Included in heading&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;MM-78&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;Title&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Included in heading&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Fix bug in API&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;Status&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Converted to TODO keyword&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;In Progress&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;Assignee&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Converted to tag&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;James Dyer&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;Priority&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Converted to org priority&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Medium&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td class=&quot;org-left&quot;&gt;Story Points&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;Extracted (not currently used in output)&lt;/td&gt;
&lt;td class=&quot;org-left&quot;&gt;3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgf6b311c&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgf6b311c&quot;&gt;Customizing for Your Organization&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgf6b311c&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgcfc2c9c&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgcfc2c9c&quot;&gt;Adjusting Issue Key Pattern&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgcfc2c9c&quot;&gt;
&lt;p&gt;
If your Jira instance uses a different project key pattern, modify the regex in &lt;code&gt;jira-to-org--parse-buffer-to-items&lt;/code&gt;:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Current pattern matches MM-123
&lt;/span&gt;((string-match &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;^&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4fb3d8;&quot;&gt;MM-[0-9]+&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4fb3d8;&quot;&gt;$&quot;&lt;/span&gt; line)&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Example: Match PROJECT-123
&lt;/span&gt;((string-match &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;^&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #4fb3d8;&quot;&gt;PROJECT-[0-9]+&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4fb3d8;&quot;&gt;$&quot;&lt;/span&gt; line)&lt;br&gt;
&lt;span style=&quot;color: #5B6268;&quot;&gt;;; &lt;/span&gt;&lt;span style=&quot;color: #5B6268;&quot;&gt;Example: Match multiple projects (PROJ1-123 or PROJ2-123)
&lt;/span&gt;((string-match &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;^&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;(?:&lt;/span&gt;&lt;span style=&quot;color: #4fb3d8;&quot;&gt;PROJ1&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color: #4fb3d8;&quot;&gt;PROJ2&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4fb3d8;&quot;&gt;-[0-9]+&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color: #78bd65; font-weight: bold;&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color: #4fb3d8;&quot;&gt;$&quot;&lt;/span&gt; line)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org1ebd581&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org1ebd581&quot;&gt;Adding Custom Status Mappings&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org1ebd581&quot;&gt;
&lt;p&gt;
If your Jira workflow has additional statuses:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; jira-to-org-status-map
      &apos;((&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;To Do&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;TODO&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;In Progress&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;DOING&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Code Review&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;REVIEW&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;QA Testing&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;TESTING&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Blocked&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;BLOCKED&quot;&lt;/span&gt;)
        (&lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;Done&quot;&lt;/span&gt; . &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;DONE&quot;&lt;/span&gt;)))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Make sure your org-mode TODO keywords match:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;setq&lt;/span&gt; org-todo-keywords
      &apos;((sequence &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;TODO&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;DOING&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;REVIEW&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;TESTING&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;BLOCKED&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;|&quot;&lt;/span&gt; &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;DONE&quot;&lt;/span&gt;)))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org254b420&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org254b420&quot;&gt;Troubleshooting&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org254b420&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org830b192&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org830b192&quot;&gt;No Items Parsed&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org830b192&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Verify the Jira data format matches the expected structure&lt;/li&gt;
&lt;li&gt;Check that issue keys match the pattern (e.g., &lt;code&gt;MM-123&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Ensure status headers (To Do, In Progress, Done) are present&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org129050f&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org129050f&quot;&gt;Wrong Assignee Tags&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org129050f&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Check &lt;code&gt;jira-to-org-assignee-map&lt;/code&gt; contains mappings for all team members&lt;/li&gt;
&lt;li&gt;Verify exact spelling of names in Jira matches the mapping&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org6ee924f&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org6ee924f&quot;&gt;Incorrect TODO Keywords&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org6ee924f&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Verify &lt;code&gt;jira-to-org-status-map&lt;/code&gt; maps all statuses used in your Jira&lt;/li&gt;
&lt;li&gt;Check that target TODO keywords exist in &lt;code&gt;org-todo-keywords&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgae8fe0c&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgae8fe0c&quot;&gt;Entries Not Found During Update&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgae8fe0c&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Ensure issue keys in org file exactly match Jira format&lt;/li&gt;
&lt;li&gt;Verify the heading contains the issue key followed by a space&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orge30aa19&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orge30aa19&quot;&gt;API Functions&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orge30aa19&quot;&gt;
&lt;p&gt;
For programmatic use:
&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org507d6f5&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org507d6f5&quot;&gt;&lt;code&gt;jira-to-org-parse-string&lt;/code&gt;&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org507d6f5&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(jira-to-org-parse-string STRING &lt;span style=&quot;color: #ee7b29;&quot;&gt;&amp;amp;optional&lt;/span&gt; SPRINT)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Parse STRING containing Jira data and return org-mode headings as a string.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgf4d2d0b&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgf4d2d0b&quot;&gt;&lt;code&gt;jira-to-org--parse-buffer-to-items&lt;/code&gt;&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgf4d2d0b&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(jira-to-org--parse-buffer-to-items BUFFER-OR-STRING)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Parse Jira data into a list of &lt;code&gt;jira-to-org-item&lt;/code&gt; structs for further processing.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-orgdc059ec&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;orgdc059ec&quot;&gt;&lt;code&gt;jira-to-org--item-to-org-heading&lt;/code&gt;&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-orgdc059ec&quot;&gt;
&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(jira-to-org--item-to-org-heading ITEM &lt;span style=&quot;color: #ee7b29;&quot;&gt;&amp;amp;optional&lt;/span&gt; SPRINT)
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;p&gt;
Convert a &lt;code&gt;jira-to-org-item&lt;/code&gt; struct to an org-mode heading string.
&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org2443076&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org2443076&quot;&gt;Contributing&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org2443076&quot;&gt;
&lt;p&gt;
Contributions are welcome! Please submit issues or pull requests on the project repository.
&lt;/p&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org17e2b6f&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org17e2b6f&quot;&gt;Development Setup&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org17e2b6f&quot;&gt;
&lt;ol class=&quot;org-ol&quot;&gt;
&lt;li&gt;Clone the repository&lt;/li&gt;
&lt;li&gt;Load &lt;code&gt;jira-to-org.el&lt;/code&gt; in Emacs&lt;/li&gt;
&lt;li&gt;Make changes and test with sample Jira data&lt;/li&gt;
&lt;li&gt;Run byte-compilation: &lt;code&gt;M-x byte-compile-file&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org16957b9&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org16957b9&quot;&gt;Testing&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org16957b9&quot;&gt;
&lt;p&gt;
Create a test file with sample Jira data and verify parsing:
&lt;/p&gt;

&lt;div class=&quot;org-src-container&quot;&gt;
&lt;pre class=&quot;src src-emacs-lisp&quot;&gt;(&lt;span style=&quot;color: #ea3d54; font-weight: bold;&quot;&gt;with-temp-buffer&lt;/span&gt;
  (insert &lt;span style=&quot;color: #4fb3d8;&quot;&gt;&quot;To Do\nMM-1\nTest Issue\nAssignee: Test User\nPriority: High\n&quot;&lt;/span&gt;)
  (jira-to-org-parse-buffer))
&lt;/pre&gt;
&lt;/div&gt;&lt;br&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-orgebc92fd&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;orgebc92fd&quot;&gt;License&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-orgebc92fd&quot;&gt;
&lt;p&gt;
This package is provided as-is for personal and professional use.
&lt;/p&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org7ed5fc9&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org7ed5fc9&quot;&gt;Changelog&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org7ed5fc9&quot;&gt;
&lt;/div&gt;
&lt;div id=&quot;outline-container-org3da7274&quot; class=&quot;outline-3&quot;&gt;
&lt;h3 id=&quot;org3da7274&quot;&gt;Version 1.0&lt;/h3&gt;
&lt;div class=&quot;outline-text-3&quot; id=&quot;text-org3da7274&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;Initial release&lt;/li&gt;
&lt;li&gt;Basic parsing of Jira sprint data&lt;/li&gt;
&lt;li&gt;Configurable mappings for assignees, priorities, and statuses&lt;/li&gt;
&lt;li&gt;Buffer, region, and clipboard parsing commands&lt;/li&gt;
&lt;li&gt;Update existing entries from fresh Jira data&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/section&gt;
&lt;section id=&quot;outline-container-org4848db4&quot; class=&quot;outline-2&quot;&gt;
&lt;h2 id=&quot;org4848db4&quot;&gt;Related Packages&lt;/h2&gt;
&lt;div class=&quot;outline-text-2&quot; id=&quot;text-org4848db4&quot;&gt;
&lt;ul class=&quot;org-ul&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ahungry/org-jira&quot;&gt;org-jira&lt;/a&gt; - Full bidirectional sync with Jira (requires API access)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/nyyManni/jiralib2&quot;&gt;jiralib2&lt;/a&gt; - Jira REST API library for Emacs&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/baohaojun/org-jira&quot;&gt;org-jira (baohaojun)&lt;/a&gt; - Alternative Jira integration&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/section&gt;
</content>
</entry>
</feed>
