<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Mike Olson - Emacs Blog Posts</title><description>Blog posts about Emacs from Mike Olson</description><link>https://mwolson.org/</link><atom:link href="https://mwolson.org/blog/emacs/rss.xml" rel="self" type="application/rss+xml" xmlns:atom="http://www.w3.org/2005/Atom"/><language>en-us</language><ttl>60</ttl><lastBuildDate>Mon, 20 Apr 2026 00:00:00 GMT</lastBuildDate><image><url>https://mwolson.org/apple-touch-icon.png</url><title>Mike Olson - Emacs Blog Posts</title><link>https://mwolson.org/</link></image><item><title>Fixing typescript-ts-mode in Emacs 30.2</title><link>https://mwolson.org/blog/2026-04-20-fixing-typescript-ts-mode-in-emacs-30-2/</link><guid isPermaLink="true">https://mwolson.org/blog/2026-04-20-fixing-typescript-ts-mode-in-emacs-30-2/</guid><description>Working around tree-sitter upgrades with a rewriter.</description><pubDate>Mon, 20 Apr 2026 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#the-symptom&quot;&gt;The Symptom&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-root-cause&quot;&gt;The Root Cause&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-approach&quot;&gt;The Approach&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#how-to-use-it&quot;&gt;How to Use It&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#caveats&quot;&gt;Caveats&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#removal-plan&quot;&gt;Removal Plan&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The Symptom&lt;/h2&gt;
&lt;p&gt;After a recent Arch update, my Emacs 30.2 + &lt;code&gt;typescript-ts-mode&lt;/code&gt; combination
started dying the first time I opened a &lt;code&gt;.ts&lt;/code&gt; or &lt;code&gt;.tsx&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Error: treesit-query-error (&quot;Invalid predicate&quot; &quot;match&quot;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The file would still display, but without any syntax highlighting.
&lt;code&gt;python-ts-mode&lt;/code&gt; exhibited the same failure. &lt;code&gt;js-ts-mode&lt;/code&gt; and &lt;code&gt;c-ts-mode&lt;/code&gt; worked
in the main buffer but had their own breakages around JSDoc ranges and C’s
emacs-specific range queries.&lt;/p&gt;
&lt;h2&gt;The Root Cause&lt;/h2&gt;
&lt;p&gt;This is &lt;a href=&quot;https://debbugs.gnu.org/cgi/bugreport.cgi?bug=79687&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Emacs bug#79687&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;,
an interaction between how Emacs 30.2 serializes tree-sitter query predicates
and what libtree-sitter 0.26 (the version shipped by Arch) accepts.&lt;/p&gt;
&lt;p&gt;Tree-sitter queries can embed predicates like &lt;code&gt;(:match &quot;^foo&quot; @name)&lt;/code&gt; to filter
captures at query-evaluation time. Emacs 30.2 serializes these s-expression
predicates to strings that look like &lt;code&gt;#match&lt;/code&gt; (no trailing &lt;code&gt;?&lt;/code&gt;), but
libtree-sitter 0.26 became strict about predicate naming and rejects unknown
names at query-parse time. The fix on Emacs &lt;code&gt;master&lt;/code&gt; (commit &lt;code&gt;b0143530&lt;/code&gt;)
switches serialization to &lt;code&gt;#match?&lt;/code&gt;, which libtree-sitter accepts. That fix has
not been backported to the &lt;code&gt;emacs-30&lt;/code&gt; branch as of 30.2.&lt;/p&gt;
&lt;p&gt;Rewriting the strings yourself doesn’t help either, because Emacs 30.2’s own
predicate dispatcher hardcodes bare &lt;code&gt;match&lt;/code&gt;/&lt;code&gt;equal&lt;/code&gt;/&lt;code&gt;pred&lt;/code&gt; and rejects
&lt;code&gt;match?&lt;/code&gt;/&lt;code&gt;equal?&lt;/code&gt;/&lt;code&gt;pred?&lt;/code&gt; at evaluation time. So any rewrite that satisfies
libtree-sitter breaks Emacs, and vice versa.&lt;/p&gt;
&lt;h2&gt;The Approach&lt;/h2&gt;
&lt;p&gt;Since neither side accepts a string-level rewrite, I work at a higher level
instead: strip the predicates entirely from queries, and move the predicate
logic into capture-name-is-a-function fontifiers.&lt;/p&gt;
&lt;p&gt;A tree-sitter font-lock rule like:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;identifier&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; @font-lock-keyword-face&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;match &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;(break&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;|continue&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; @font-lock-keyword-face&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;gets rewritten to:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;identifier&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; @my-ts-rw--fn-font-lock-keyword-face-abc12345&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where the auto-generated function &lt;code&gt;my-ts-rw--fn-font-lock-keyword-face-abc12345&lt;/code&gt;
applies &lt;code&gt;font-lock-keyword-face&lt;/code&gt; to the node only if the node’s text matches the
original regex. The resulting query contains no predicates, so libtree-sitter is
happy; the fontifier applies the face only when the original predicate would
have matched, so the semantics are preserved.&lt;/p&gt;
&lt;p&gt;The rewrite happens via &lt;code&gt;:filter-args&lt;/code&gt; advice on three Emacs functions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;treesit-font-lock-rules&lt;/code&gt; is the main call path for font-lock rules and covers
nearly all modes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;treesit-range-rules&lt;/code&gt; is used by &lt;code&gt;js-ts-mode&lt;/code&gt; (and others) to embed a JSDoc
parser inside comment nodes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;treesit-query-compile&lt;/code&gt; catches modes like &lt;code&gt;c-ts-mode&lt;/code&gt; that compile queries
directly with an s-expression containing &lt;code&gt;:match&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How to Use It&lt;/h2&gt;
&lt;p&gt;The workaround lives in a single file in my
&lt;a href=&quot;https://github.com/mwolson/emacs-shared&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;emacs-shared&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; repo:
&lt;a href=&quot;https://github.com/mwolson/emacs-shared/blob/master/init/treesit-predicate-rewrite.el&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;init/treesit-predicate-rewrite.el&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Drop the file somewhere on your load path and load it early, before any
tree-sitter mode runs its font-lock setup:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;load&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/path/to/treesit-predicate-rewrite&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It self-activates via &lt;code&gt;define-advice&lt;/code&gt;, so there’s no setup call to make. The
advice is a no-op on queries that don’t contain predicates, so it’s safe to
leave on even after the bug is fixed upstream.&lt;/p&gt;
&lt;h2&gt;Caveats&lt;/h2&gt;
&lt;p&gt;The rewriter handles three cases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Predicate targets a face capture.&lt;/strong&gt; Rewrites into a fontifier as shown
above. This applies to the vast majority of uses in &lt;code&gt;typescript-ts-mode&lt;/code&gt;,
&lt;code&gt;python-ts-mode&lt;/code&gt;, and friends.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;outer group wraps an inner scratch capture&lt;/strong&gt;, a pattern used by
&lt;code&gt;ruby-ts-mode&lt;/code&gt; where the face lives on the outer group and the predicate
tests a scratch capture inside. Flattened and then handled as case 1.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Predicate targets a non-face capture.&lt;/strong&gt; The predicate is silently stripped,
which means the fontifier will over-match. &lt;code&gt;elixir-ts-mode&lt;/code&gt; uses this pattern
heavily. In practice the visual regression is minor, but if it bothers you,
set &lt;code&gt;my-ts-rw-verbose&lt;/code&gt; to &lt;code&gt;t&lt;/code&gt; to log strips.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;:equal&lt;/code&gt; predicates are handled for cases 1 and 2. &lt;code&gt;:pred&lt;/code&gt; falls back to strip
(case 3) since replicating an arbitrary user function inside a fontifier is more
trouble than it’s worth.&lt;/p&gt;
&lt;p&gt;I’ve verified the fix on &lt;code&gt;typescript-ts-mode&lt;/code&gt;, &lt;code&gt;tsx-ts-mode&lt;/code&gt;, &lt;code&gt;python-ts-mode&lt;/code&gt;,
&lt;code&gt;js-ts-mode&lt;/code&gt;, &lt;code&gt;c-ts-mode&lt;/code&gt;, &lt;code&gt;rust-ts-mode&lt;/code&gt;, &lt;code&gt;java-ts-mode&lt;/code&gt;, &lt;code&gt;go-ts-mode&lt;/code&gt;, and
&lt;code&gt;lua-ts-mode&lt;/code&gt;. All load and fontify without errors.&lt;/p&gt;
&lt;h2&gt;Removal Plan&lt;/h2&gt;
&lt;p&gt;Once I upgrade to an Emacs that carries the bug#79687 fix (Emacs 31, or a
backport into a future 30.x), I’ll delete the file and the &lt;code&gt;load&lt;/code&gt; line. Until
then, it’s one file and one &lt;code&gt;load&lt;/code&gt; line, so the maintenance cost is low.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item><item><title>eglot-python-preset and eglot-typescript-preset: Now on MELPA</title><link>https://mwolson.org/blog/2026-04-02-eglot-python-preset-and-eglot-typescript-preset-now-on-melpa/</link><guid isPermaLink="true">https://mwolson.org/blog/2026-04-02-eglot-python-preset-and-eglot-typescript-preset-now-on-melpa/</guid><description>Both Emacs packages for Python and TypeScript LSP configuration are now on MELPA, with multi-LSP support via rass included out of the box.</description><pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#what-changed&quot;&gt;What Changed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#installation&quot;&gt;Installation&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#python&quot;&gt;Python&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#typescript&quot;&gt;TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#out-of-the-box-multi-lsp&quot;&gt;Out-of-the-Box Multi-LSP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#per-project-configuration&quot;&gt;Per-Project Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#links&quot;&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;A quick follow-up to my earlier posts on
&lt;a href=&quot;https://mwolson.org/blog/2026-01-11-announcing-eglot-python-preset/&quot;&gt;eglot-python-preset&lt;/a&gt; and the
&lt;a href=&quot;https://mwolson.org/blog/2026-03-16-beta-emacs-multi-lsp-support-for-python-and-typescript-frameworks/&quot;&gt;beta multi-LSP release&lt;/a&gt;:
both packages are now available on MELPA with full
&lt;a href=&quot;https://github.com/joaotavora/rassumfrassum&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rassumfrassum&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; (rass) support
included.&lt;/p&gt;
&lt;p&gt;When I wrote the beta post in March, &lt;code&gt;eglot-typescript-preset&lt;/code&gt; wasn’t on MELPA
at all, and &lt;code&gt;eglot-python-preset&lt;/code&gt;’s MELPA version didn’t include rass support.
That’s no longer the case. You can install both with a standard &lt;code&gt;use-package&lt;/code&gt;
&lt;code&gt;:ensure t&lt;/code&gt; and get multi-LSP out of the box.&lt;/p&gt;
&lt;h2&gt;What Changed&lt;/h2&gt;
&lt;p&gt;The main thing that landed since the beta: MELPA now ships the rass backend for
both packages. This means the &lt;code&gt;rass&lt;/code&gt; LSP server option and all the preset
generation machinery are available without cloning repos or managing load paths
manually.&lt;/p&gt;
&lt;p&gt;Both packages also now set up Eglot integration automatically when Eglot loads,
so you no longer need an explicit &lt;code&gt;(eglot-python-preset-setup)&lt;/code&gt; or
&lt;code&gt;(eglot-typescript-preset-setup)&lt;/code&gt; call in your config. Just install and go.&lt;/p&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;h3&gt;Python&lt;/h3&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;use-package&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; eglot-python-preset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ensure t&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;custom&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;ty&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt; ; or &apos;basedpyright or &apos;rass&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the default &lt;code&gt;ty&lt;/code&gt; backend, this gives you type checking for standard Python
projects and PEP-723 scripts with no further configuration. Switch to &lt;code&gt;rass&lt;/code&gt; to
run ty and Ruff simultaneously:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;use-package&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; eglot-python-preset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ensure t&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;custom&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-rass-tools &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ty ruff&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;TypeScript&lt;/h3&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;use-package&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; eglot-typescript-preset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ensure t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s it. The defaults configure &lt;code&gt;typescript-language-server&lt;/code&gt; for TS/JS files
and use rass to combine language servers with Tailwind CSS support for CSS,
Astro, Vue, and Svelte buffers. If your project uses any of those frameworks,
the corresponding language server starts automatically when you open a file in
that mode.&lt;/p&gt;
&lt;p&gt;To add linting alongside the TypeScript language server, switch the TS/JS
backend to rass:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;use-package&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; eglot-typescript-preset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ensure t&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;custom&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-rass-tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;   &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;typescript-language-server eslint&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Out-of-the-Box Multi-LSP&lt;/h2&gt;
&lt;p&gt;The part I’m most pleased with is how much works without any rass-specific
configuration on the TypeScript side. The default settings for CSS, Astro, Vue,
and Svelte all use rass, combining each framework’s language server with
Tailwind CSS support automatically.&lt;/p&gt;
&lt;p&gt;For Astro, this means &lt;code&gt;astro-ls&lt;/code&gt; runs alongside ESLint and
&lt;code&gt;tailwindcss-language-server&lt;/code&gt; in a single Eglot session. Vue gets hybrid mode
where &lt;code&gt;vue-language-server&lt;/code&gt; handles template features and
&lt;code&gt;typescript-language-server&lt;/code&gt; with &lt;code&gt;@vue/typescript-plugin&lt;/code&gt; provides type
checking, plus Tailwind. Svelte works similarly.&lt;/p&gt;
&lt;p&gt;On the Python side, the &lt;code&gt;rass&lt;/code&gt; backend lets you run ty for type checking and
Ruff for linting in the same buffer. PEP-723 scripts work too: the generated
rass preset includes the script’s cached uv environment so both tools resolve
imports.&lt;/p&gt;
&lt;p&gt;All of this resolves executables from project-local directories first
(&lt;code&gt;node_modules/.bin&lt;/code&gt; for JS/TS, &lt;code&gt;.venv&lt;/code&gt; for Python), falling back to your PATH.&lt;/p&gt;
&lt;h2&gt;Per-Project Configuration&lt;/h2&gt;
&lt;p&gt;Both packages support per-project overrides via &lt;code&gt;.dir-locals.el&lt;/code&gt; or
&lt;code&gt;dir-locals-set-directory-class&lt;/code&gt;. The preset variables are declared as safe
local variables, so Emacs applies them without prompting. For example, if one
project uses Biome instead of ESLint:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;;; .dir-locals.el&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;typescript-ts-mode&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;     (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-rass-tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;typescript-language-server biome&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mwolson/eglot-python-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-python-preset&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; on
GitHub&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mwolson/eglot-typescript-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-typescript-preset&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
on GitHub&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/joaotavora/rassumfrassum&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rassumfrassum&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, the LSP
multiplexer by Eglot’s author&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mwolson.org/blog/2026-01-11-announcing-eglot-python-preset/&quot;&gt;Announcing eglot-python-preset&lt;/a&gt;,
covering the initial release&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mwolson.org/blog/2026-03-16-beta-emacs-multi-lsp-support-for-python-and-typescript-frameworks/&quot;&gt;Beta: Multi-LSP support&lt;/a&gt;,
covering the rass integration and eglot-typescript-preset introduction&lt;/li&gt;
&lt;/ul&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item><item><title>Beta: Emacs Multi-LSP support for Python and Typescript frameworks</title><link>https://mwolson.org/blog/2026-03-16-beta-emacs-multi-lsp-support-for-python-and-typescript-frameworks/</link><guid isPermaLink="true">https://mwolson.org/blog/2026-03-16-beta-emacs-multi-lsp-support-for-python-and-typescript-frameworks/</guid><description>Multi-language-server support for Python and TypeScript in Emacs, with easy per-project configuration.</description><pubDate>Mon, 16 Mar 2026 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#why-rass&quot;&gt;Why rass?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#python-ty--ruff-in-one-buffer&quot;&gt;Python: ty + Ruff in One Buffer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#typescript-multi-framework-lsp-support&quot;&gt;TypeScript: Multi-Framework LSP Support&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#astro--eslint--oxlint&quot;&gt;Astro + ESLint + oxlint&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#supported-frameworks&quot;&gt;Supported Frameworks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#per-project-configuration&quot;&gt;Per-Project Configuration&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#in-repo-dir-localsel&quot;&gt;In-repo: &lt;code&gt;.dir-locals.el&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#out-of-repo-dir-locals-set-directory-class&quot;&gt;Out-of-repo: &lt;code&gt;dir-locals-set-directory-class&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#installation&quot;&gt;Installation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#feedback-welcome&quot;&gt;Feedback Welcome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#links&quot;&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Back in January I
&lt;a href=&quot;https://mwolson.org/blog/2026-01-11-announcing-eglot-python-preset/&quot;&gt;announced eglot-python-preset&lt;/a&gt;,
an Emacs package that simplifies Python LSP configuration with Eglot. At the
time, I mentioned rass support as a future direction. That future has arrived,
and it brought a companion package along with it.&lt;/p&gt;
&lt;p&gt;Both &lt;a href=&quot;https://github.com/mwolson/eglot-python-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-python-preset&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; and
the new
&lt;a href=&quot;https://github.com/mwolson/eglot-typescript-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-typescript-preset&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
now support &lt;a href=&quot;https://github.com/joaotavora/rassumfrassum&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rassumfrassum&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; (rass),
an LSP multiplexer written by Eglot’s author. This means you can run multiple
language servers simultaneously through a single Eglot connection, such as a
type checker alongside a linter or a language server alongside a formatter, with
minimal configuration.&lt;/p&gt;
&lt;p&gt;The eglot-typescript-preset package covers Astro, Vue, Svelte, Deno, and
CSS/Tailwind out of the box. On the linting side, it integrates with ESLint,
oxlint, Biome, and oxfmt through rass, so you can combine a type checker with
one or more linters in the same Eglot session.&lt;/p&gt;
&lt;aside aria-label=&quot;Update (Apr 2 2026)&quot; class=&quot;admonition&quot; data-admonition-type=&quot;note&quot;&gt;&lt;p class=&quot;admonition-title&quot; aria-hidden=&quot;true&quot;&gt;Update (Apr 2 2026)&lt;/p&gt;&lt;div class=&quot;admonition-content&quot;&gt;&lt;p&gt;Both packages are now on MELPA with full rass support. See
&lt;a href=&quot;https://mwolson.org/blog/2026-04-02-eglot-python-preset-and-eglot-typescript-preset-now-on-melpa/&quot;&gt;eglot-python-preset and eglot-typescript-preset: Now on MELPA&lt;/a&gt;
for details.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;This is a &lt;strong&gt;beta release&lt;/strong&gt;. The rass backend works well in my day-to-day use,
but the MELPA packages don’t support rass yet. I have pending PRs with MELPA for
both packages, and &lt;code&gt;eglot-typescript-preset&lt;/code&gt; isn’t on MELPA at all yet. For now,
manual installation (cloning the repos) is the way to go if you want to try rass
support.&lt;/p&gt;
&lt;h2&gt;Why rass?&lt;/h2&gt;
&lt;p&gt;Eglot is designed around one language server per major mode. If you want type
checking from ty and linting from Ruff in the same Python buffer, or TypeScript
diagnostics alongside ESLint, you’d normally have to choose one or run a
separate tool outside the LSP workflow.&lt;/p&gt;
&lt;p&gt;rass solves this by acting as a stdio multiplexer: it presents itself as a
single LSP server to Eglot while dispatching requests to multiple backend tools.
From Eglot’s perspective, nothing changes. From your perspective, you get
diagnostics, completions, and code actions from all your tools in one place.&lt;/p&gt;
&lt;p&gt;The preset packages handle the rass configuration for you. You declare which
tools you want, and the package generates the appropriate rass preset file
automatically. When your project has tools installed locally (in &lt;code&gt;.venv&lt;/code&gt; or
&lt;code&gt;node_modules&lt;/code&gt;), those executables are preferred over global installations.&lt;/p&gt;
&lt;h2&gt;Python: ty + Ruff in One Buffer&lt;/h2&gt;
&lt;p&gt;To use rass with &lt;code&gt;eglot-python-preset&lt;/code&gt;, set the LSP server to &lt;code&gt;rass&lt;/code&gt; and list
your tools:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;use-package&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; eglot-python-preset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ensure t&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;after eglot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;custom&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-rass-tools &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ty ruff&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-setup&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives you ty’s type checking and Ruff’s linting diagnostics in every Python
buffer, with zero per-project setup for standard projects. PEP-723 scripts work
too: the generated rass preset includes the script’s cached uv environment so
both tools resolve imports correctly.&lt;/p&gt;
&lt;p&gt;You can also use basedpyright instead of ty, or pass literal command vectors for
custom tools:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt eglot-python-preset-rass-tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;        &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;basedpyright&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;          ruff&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;          [&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;custom-lsp&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;--stdio&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;]))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;TypeScript: Multi-Framework LSP Support&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;eglot-typescript-preset&lt;/code&gt; is brand new. It configures Eglot for TypeScript,
JavaScript, CSS, and several frontend frameworks, with rass support for
combining tools like ESLint, Biome, oxlint, and oxfmt alongside the TypeScript
language server.&lt;/p&gt;
&lt;p&gt;The default setup requires no rass and works out of the box:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;use-package&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; eglot-typescript-preset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ensure t&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;after eglot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-setup&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This configures &lt;code&gt;typescript-language-server&lt;/code&gt; for TS/JS files, &lt;code&gt;astro-ls&lt;/code&gt; for
Astro files, and &lt;code&gt;vscode-css-language-server&lt;/code&gt; with &lt;code&gt;tailwindcss-language-server&lt;/code&gt;
for CSS, all automatically.&lt;/p&gt;
&lt;h3&gt;Astro + ESLint + oxlint&lt;/h3&gt;
&lt;p&gt;One setup I’m particularly happy with is a fully-working Astro project with both
ESLint and oxlint. The idea is that oxlint handles the fast, low-level lint
rules while ESLint covers the Astro-specific and framework-aware rules. To avoid
duplicate diagnostics, the
&lt;a href=&quot;https://www.npmjs.com/package/eslint-plugin-oxlint&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eslint-plugin-oxlint&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
package disables ESLint rules that oxlint already covers.&lt;/p&gt;
&lt;p&gt;On the Emacs side, the configuration is straightforward:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt eglot-typescript-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt eglot-typescript-preset-rass-tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;        &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;typescript-language-server eslint oxlint&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt eglot-typescript-preset-astro-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt eglot-typescript-preset-astro-rass-tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;        &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;astro-ls eslint oxlint&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All three tools contribute diagnostics to the same Eglot session, and the preset
handles executable resolution from your project’s &lt;code&gt;node_modules&lt;/code&gt; automatically.&lt;/p&gt;
&lt;h3&gt;Supported Frameworks&lt;/h3&gt;
&lt;p&gt;Beyond plain TypeScript and JavaScript, &lt;code&gt;eglot-typescript-preset&lt;/code&gt; supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Astro&lt;/strong&gt; via &lt;code&gt;astro-ls&lt;/code&gt;, with automatic TypeScript SDK detection&lt;/li&gt;
&lt;li&gt;CSS support via &lt;code&gt;vscode-css-language-server&lt;/code&gt;, optionally combined with
&lt;code&gt;tailwindcss-language-server&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deno&lt;/strong&gt; as an alternative backend for Deno-based projects&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Svelte&lt;/strong&gt; via &lt;code&gt;svelte-language-server&lt;/code&gt;, with TypeScript SDK path forwarding&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vue&lt;/strong&gt; via &lt;code&gt;vue-language-server&lt;/code&gt; in hybrid mode, where Vue handles template
features and &lt;code&gt;typescript-language-server&lt;/code&gt; with &lt;code&gt;@vue/typescript-plugin&lt;/code&gt;
provides type checking&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Per-Project Configuration&lt;/h2&gt;
&lt;p&gt;Both packages support per-project overrides, so different projects can use
different tools. There are two approaches.&lt;/p&gt;
&lt;h3&gt;In-repo: &lt;code&gt;.dir-locals.el&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Drop a &lt;code&gt;.dir-locals.el&lt;/code&gt; in the project root. The preset packages declare their
variables as safe with appropriate values, so Emacs applies them without
prompting:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;;; .dir-locals.el for a Python project using basedpyright + ruff&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;python-ts-mode&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;     (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-rass-tools &lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;basedpyright ruff&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;;; .dir-locals.el for a TypeScript project using biome instead of eslint&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;typescript-ts-mode&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;  .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;     (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-rass-tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;      .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;typescript-language-server biome&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Out-of-repo: &lt;code&gt;dir-locals-set-directory-class&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;If you’d rather not add Emacs-specific files to a project, configure it from
your init file:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;dir-locals-set-class-variables&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-python-project&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;python-ts-mode&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;       (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-rass-tools &lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ty ruff&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))))))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;dir-locals-set-directory-class&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;expand-file-name&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;~/devel/my-python-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-python-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;dir-locals-set-class-variables&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-astro-project&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;typescript-ts-mode&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;       (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-rass-tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;typescript-language-server eslint oxlint&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;   (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;astro-ts-mode&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;    .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-astro-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;       (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-astro-rass-tools&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;        .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;astro-ls eslint oxlint&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))))))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;dir-locals-set-directory-class&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;expand-file-name&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;~/devel/my-astro-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-astro-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Installation&lt;/h2&gt;
&lt;p&gt;Of note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;rass backend works&lt;/strong&gt; and has fairly extensive tests.&lt;/li&gt;
&lt;li&gt;MELPA packages &lt;strong&gt;don’t support rass yet&lt;/strong&gt;. I have pending PRs with MELPA for
both packages. Until those land, you need to install from source.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;eglot-typescript-preset&lt;/code&gt; package &lt;strong&gt;isn’t on MELPA at all&lt;/strong&gt; yet. A
submission is pending.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To install manually:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; ~/devel&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; https://github.com/mwolson/eglot-python-preset&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; ~/devel/eglot-python-preset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; https://github.com/mwolson/eglot-typescript-preset&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; ~/devel/eglot-typescript-preset&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;load-path&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;expand-file-name&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;~/devel/eglot-python-preset&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;load-path&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;expand-file-name&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;~/devel/eglot-typescript-preset&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;require&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt eglot-python-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt eglot-python-preset-rass-tools &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ty ruff&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-setup&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;require&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt eglot-typescript-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt eglot-typescript-preset-rass-tools &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;typescript-language-server eslint&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-typescript-preset-setup&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both packages require Emacs 30.2 or later and
&lt;a href=&quot;https://github.com/joaotavora/rassumfrassum&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rass&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; v0.3.3 or later.&lt;/p&gt;
&lt;h2&gt;Feedback Welcome&lt;/h2&gt;
&lt;p&gt;If you try either package, I’d appreciate hearing about your experience. The
best way to reach me is through GitHub Discussions on the respective repos:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mwolson/eglot-python-preset/discussions&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-python-preset discussions&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mwolson/eglot-typescript-preset/discussions&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-typescript-preset discussions&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In particular, I’d love to hear from people using the JS/TS frameworks we
support (Astro, Vue, Svelte, and Deno) as well as anyone working with CSS and
Tailwind. If there’s a commonly-used framework or tool you’d like to see
supported, please open a discussion. The more real-world usage these packages
get, the more confident I’ll be promoting them from beta.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mwolson/eglot-python-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-python-preset&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; on
GitHub&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mwolson/eglot-typescript-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-typescript-preset&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
on GitHub&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/joaotavora/rassumfrassum&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rassumfrassum&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, the LSP
multiplexer that makes multi-server setups work&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mwolson.org/blog/2026-01-11-announcing-eglot-python-preset/&quot;&gt;Announcing eglot-python-preset&lt;/a&gt;,
the earlier post covering the initial release&lt;/li&gt;
&lt;/ul&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item><item><title>First Week with Jujutsu VCS</title><link>https://mwolson.org/blog/2026-01-22-first-week-with-jujutsu-vcs/</link><guid isPermaLink="true">https://mwolson.org/blog/2026-01-22-first-week-with-jujutsu-vcs/</guid><description>Configuration tips and Emacs integration for Jujutsu after a week of use.</description><pubDate>Thu, 22 Jan 2026 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;p&gt;I’ve spent my first week using &lt;a href=&quot;https://docs.jj-vcs.dev/latest/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Jujutsu&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; (jj),
a Git-compatible version control system that takes a different approach to
managing commits and branches. Here are my impressions and some configuration
tips I’ve picked up along the way.&lt;/p&gt;
&lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#configuring-immutable-bookmarks&quot;&gt;Configuring Immutable Bookmarks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#emacs-integration-with-majutsu&quot;&gt;Emacs Integration with Majutsu&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#rebasing-stacked-branches&quot;&gt;Rebasing Stacked Branches&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#cleaning-up-after-rebases&quot;&gt;Cleaning Up After Rebases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#magit-and-jj---conflict-resolution&quot;&gt;Magit and jj - Conflict Resolution&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#magit-and-jj----branch&quot;&gt;Magit and jj - ’@’ Branch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#overall-impressions&quot;&gt;Overall Impressions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Configuring Immutable Bookmarks&lt;/h2&gt;
&lt;p&gt;One of the first things I changed was the default immutability settings.
Jujutsu’s default configuration locks down remote bookmarks (which are
“branches” in Git terms) as immutable, which prevents accidental modifications
but can be overly restrictive.&lt;/p&gt;
&lt;p&gt;I added this to &lt;code&gt;~/.config/jj/config.toml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;[&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;revset-aliases&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;immutable_heads()&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;present(trunk()) | tags()&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This relaxes the immutability rules to only protect the &lt;code&gt;trunk&lt;/code&gt; (the default
branch, typically &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; from the backing Git repo) and tags, giving
me more flexibility to work with feature branches that have been pushed to
remotes.&lt;/p&gt;
&lt;h2&gt;Emacs Integration with Majutsu&lt;/h2&gt;
&lt;p&gt;For Emacs integration, I’m using &lt;a href=&quot;https://github.com/0WD0/majutsu&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;majutsu&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;,
which provides a Magit-like interface for Jujutsu. I configured it to start in a
log buffer showing the branches I care about, with the ability to hit &lt;kbd&gt;
Enter&lt;/kbd&gt; to visit a commit or &lt;kbd&gt;O&lt;/kbd&gt; to start a new commit at that
point.&lt;/p&gt;
&lt;p&gt;Here’s my configuration from my
&lt;a href=&quot;https://github.com/mwolson/emacs-shared/blob/master/init/shared-init.el#L2092&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;shared Emacs init&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-replace-cdrs-in-alist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt;old-el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt; new-el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt; alist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Replace cdr instances of OLD-EL with NEW-EL in ALIST.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;mapc&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;lambda&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt;el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;            (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;when&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;eq&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;cdr&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; old-el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;              (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;setcdr&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; el new-el&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;        (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;symbol-value&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; alist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; Set up majutsu&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-eval-after-load&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;majutsu&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;  ;; Replace pop-to-buffer with switch-to-buffer for same-window behavior&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-replace-cdrs-in-alist &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;pop-to-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;switch-to-buffer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;                            &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;majutsu-display-functions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt majutsu-default-display-function &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;#&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;switch-to-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-majutsu-log&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #9D7CD8);--shiki-dark:#9D7CD8;--shiki-dark-font-style:italic;--shiki-light:#8839EF;--shiki-light-font-style:inherit&quot;&gt;interactive&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;majutsu-log &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;or&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-project-root&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; default-directory&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; Set up keybinds&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-eval-after-load&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set project-prefix-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;j&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-majutsu-log&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-global-set &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-x V j&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-majutsu-log&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key customizations here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Same-window display&lt;/strong&gt;: I replaced &lt;code&gt;pop-to-buffer&lt;/code&gt; with &lt;code&gt;switch-to-buffer&lt;/code&gt; so
that majutsu buffers open in the current window rather than splitting. This
matches my preferred Magit workflow.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;project-aware log&lt;/strong&gt;: The &lt;code&gt;my-majutsu-log&lt;/code&gt; function opens the log at the
detected project root, which I bind to &lt;kbd&gt;C-x V j&lt;/kbd&gt; in my global keymap,
or &lt;kbd&gt;j&lt;/kbd&gt; when visiting a project. It will also offer to initialize jj
if I haven’t done so yet.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Rebasing Stacked Branches&lt;/h2&gt;
&lt;p&gt;Rebasing stacked branches onto an updated main branch is quite nice with
Jujutsu:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; fetch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Rebase all commits on feature-branch that aren&apos;t already in main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; rebase&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -b&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; feature-branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-b&lt;/code&gt; flag specifies the branch to rebase, and &lt;code&gt;-o&lt;/code&gt; specifies the new base.
Jujutsu handles the rebase intelligently, only moving commits that aren’t
already part of main, and automatically brings over multiple bookmarks that may
be on that branch.&lt;/p&gt;
&lt;p&gt;I also have a script fragment that can update all tracked bookmarks on all Git
submodules:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# excerpt - assume $d is current submodule&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -f&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$topdir&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/.gitmodules&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; submodule.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$d&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; main&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; track&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --remote=origin&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; 2&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt; true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; git&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; fetch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Check for bookmark conflicts after fetch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt; jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; 2&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;conflict&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Warning: Bookmark conflicts detected in &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$d&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Run &apos;cd &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$d&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; &amp;amp;&amp;amp; jj bookmark list -t&apos; to see details&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;    echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Use &apos;jj bookmark track &amp;lt;bookmark&amp;gt; --remote=origin&apos; to resolve&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Rebase all tracked bookmarks (except main) onto updated main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Get list of tracked bookmarks, excluding the main branch and @origin suffixes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;other_bookmarks&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;$(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; 2&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;    grep&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -v&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;^  &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;    awk&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;{print $1}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;    sed&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;s/:$//&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;    grep&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -v&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;^&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt;\$&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt; true&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; [[&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#179299&quot;&gt; -n&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$other_bookmarks&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ]];&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;    for&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; $other_bookmarks&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; do&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;        # Skip if bookmark name is empty or contains @origin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;        [[&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#179299&quot;&gt; -z&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#179299&quot;&gt; ||&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#179299&quot;&gt; ==&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;@&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ]]&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &amp;amp;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; continue&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;        # Check if bookmark has commits not in main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;        if&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt; jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; log&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;..&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --limit&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; 2&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/dev/null&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt; grep&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -q&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;            echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Rebasing &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; onto &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;            if&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; !&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt; jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; rebase&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -b&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;main_branch&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; 2&amp;gt;&amp;amp;1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt; then&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;                echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Warning: Failed to rebase &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;${&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;bookmark&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#4C4F69&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; - may have conflicts&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#D20F39;--shiki-light-font-style:italic&quot;&gt;                echo&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Run &apos;cd &lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;$d&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; &amp;amp;&amp;amp; jj log -r &lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;conflict()&lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt;\&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;&apos; to see conflicted commits&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;            fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;        fi&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;    done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Cleaning Up After Rebases&lt;/h2&gt;
&lt;p&gt;One minor annoyance: after rebasing, you may end up with orphaned bookmarks in
your &lt;code&gt;jj log&lt;/code&gt; output that clutter the view. You’ll need to manually clean these
up with &lt;code&gt;jj abandon&lt;/code&gt; to keep a tidy log. It can be a bit tedious, but so far I’m
finding that it’s worth the effort to maintain a clear picture of which stacks
haven’t been merged yet.&lt;/p&gt;
&lt;h2&gt;Magit and jj - Conflict Resolution&lt;/h2&gt;
&lt;p&gt;Here’s an important gotcha I discovered: if you’re in the middle of resolving a
Jujutsu merge and you run &lt;code&gt;magit-status&lt;/code&gt;, Emacs can slow to a crawl. Magit will
show hundreds of conflicting symlinks (which is how jj represents conflicts in
the working directory), overwhelming the buffer.&lt;/p&gt;
&lt;p&gt;The best practice is to stay in Jujutsu’s tooling once you start resolving
conflicts there. Don’t switch to Magit mid-resolution.&lt;/p&gt;
&lt;p&gt;This situation commonly arises when squash-merging PRs for a branch that is
stacked on another. GitHub’s squash merge creates a new commit that doesn’t
match the commits jj knows about, which can leave your local branch in a state
where jj thinks it needs to merge.&lt;/p&gt;
&lt;p&gt;When this happens, it can save significant time to rebase using a specific
commit ID rather than a bookmark name:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Find the commit ID of main after the squash merge&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; log&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -r&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;bookmar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;k&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;# Rebase your bookmark starting at that specific commit onto main&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;jj&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; rebase&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -s&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; &amp;lt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;earliest-commit-id-of-bookmar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;k&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This explicitly tells jj which commit to move and where, avoiding the merge
conflicts that arise from the squash merge mismatch. I do something similar with
subset-rebases in Magit when using Git.&lt;/p&gt;
&lt;h2&gt;Magit and jj - ’@’ Branch&lt;/h2&gt;
&lt;p&gt;Using jj and then using Magit can cause Magit to be confused about what branch
it’s on. Unfortunately because of the existence of &lt;code&gt;@&lt;/code&gt; as a branch, which comes
before the typical name of the branch when showing a revision, Magit treats &lt;code&gt;@&lt;/code&gt;
as the one that’s meaningful and doesn’t show the merge/push target for the
branch I actually care about. I haven’t yet found a good solution, other than
manually specifying where to push. Maybe Magit could be advised to ignore that
branch at some point.&lt;/p&gt;
&lt;h2&gt;Overall Impressions&lt;/h2&gt;
&lt;p&gt;After a week, I’m finding Jujutsu’s model fairly intuitive once you get past the
initial learning curve, though I’m still using Git most of the time. The
automatic tracking of the working copy as a commit, the powerful revset
language, and the Git compatibility does make jj an interesting alternative to
keep learning.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item><item><title>Announcing eglot-python-preset</title><link>https://mwolson.org/blog/2026-01-11-announcing-eglot-python-preset/</link><guid isPermaLink="true">https://mwolson.org/blog/2026-01-11-announcing-eglot-python-preset/</guid><description>A new Emacs package for Python LSP support with PEP-723 scripts.</description><pubDate>Sun, 11 Jan 2026 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#what-eglot-python-preset-does&quot;&gt;What eglot-python-preset Does&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#basic-setup&quot;&gt;Basic Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#key-commands&quot;&gt;Key Commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#future-directions&quot;&gt;Future Directions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#links&quot;&gt;Links&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;I’m happy to announce the release of
&lt;a href=&quot;https://github.com/mwolson/eglot-python-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-python-preset&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, a new
Emacs package that simplifies Python LSP configuration with Eglot. It’s now
available on MELPA.&lt;/p&gt;
&lt;p&gt;The package was born from a
&lt;a href=&quot;https://www.reddit.com/r/emacs/comments/1py6v5z/using_emacs_for_python_development_with_uv_and/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;discussion on r/emacs&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
about using Emacs for Python development with uv and basedpyright. The original
poster had a common pain point: they were writing standalone Python scripts with
&lt;a href=&quot;https://peps.python.org/pep-0723/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;PEP-723&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; inline dependencies, using
&lt;code&gt;uv run script.py&lt;/code&gt; to execute them. Everything worked fine from the command
line, but their LSP (basedpyright) couldn’t resolve the imports because it had
no awareness of uv’s cached environments.&lt;/p&gt;
&lt;p&gt;This is a gap in the tooling. uv manages isolated environments for PEP-723
scripts automatically, but there was no bridge between those environments and
the editor’s language server. You’d get &lt;code&gt;reportMissingImports&lt;/code&gt; errors for
packages that were clearly installed and working at runtime.&lt;/p&gt;
&lt;h2&gt;What eglot-python-preset Does&lt;/h2&gt;
&lt;p&gt;The package handles the glue between uv, PEP-723 scripts, and your LSP server.
When you open a Python file containing PEP-723 metadata:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It detects the script metadata automatically&lt;/li&gt;
&lt;li&gt;It locates the cached uv environment for that script (or prompts you to
create one)&lt;/li&gt;
&lt;li&gt;It configures the LSP server to use that environment for type checking&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For standard Python projects with &lt;code&gt;pyproject.toml&lt;/code&gt; or &lt;code&gt;requirements.txt&lt;/code&gt;, the
package handles project root detection so Eglot starts from the right directory.&lt;/p&gt;
&lt;h2&gt;Basic Setup&lt;/h2&gt;
&lt;p&gt;Installation from MELPA:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;use-package&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; eglot-python-preset&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;ensure t&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;after eglot&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;custom&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-lsp-server &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;ty&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt; ; or &apos;basedpyright&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;  :&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;eglot-python-preset-setup&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The package supports both &lt;a href=&quot;https://github.com/astral-sh/ty&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ty&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; (Astral’s new
Rust-based type checker) and
&lt;a href=&quot;https://github.com/DetachHead/basedpyright&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;basedpyright&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;. See my
&lt;a href=&quot;https://mwolson.org/blog/2025-12-17-ty-a-fast-python-type-checker-and-lsp-for-emacs/&quot;&gt;earlier post on ty&lt;/a&gt;
for why you might want to try ty — it’s dramatically faster than the
alternatives.&lt;/p&gt;
&lt;h2&gt;Key Commands&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;M-x eglot-python-preset-sync-environment&lt;/code&gt;&lt;/strong&gt; - Sync dependencies for a
PEP-723 script using &lt;code&gt;uv sync --script&lt;/code&gt;, then restart Eglot. Use this after
adding or modifying dependencies in your script’s metadata block.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;M-x eglot-python-preset-remove-environment&lt;/code&gt;&lt;/strong&gt; - Remove the cached
environment to force a clean reinstall.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;M-x eglot-python-preset-run-script&lt;/code&gt;&lt;/strong&gt; - Run the current script using
&lt;code&gt;uv run&lt;/code&gt; in a compilation buffer.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Future Directions&lt;/h2&gt;
&lt;aside aria-label=&quot;Update (Apr 2 2026)&quot; class=&quot;admonition&quot; data-admonition-type=&quot;note&quot;&gt;&lt;p class=&quot;admonition-title&quot; aria-hidden=&quot;true&quot;&gt;Update (Apr 2 2026)&lt;/p&gt;&lt;div class=&quot;admonition-content&quot;&gt;&lt;p&gt;Both eglot-python-preset and eglot-typescript-preset (a companion package for
Typescript and web frameworks) are now on MELPA with full rass support. See
&lt;a href=&quot;https://mwolson.org/blog/2026-04-02-eglot-python-preset-and-eglot-typescript-preset-now-on-melpa/&quot;&gt;eglot-python-preset and eglot-typescript-preset: Now on MELPA&lt;/a&gt;
for details.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;There are two potential follow-ups I’m considering:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Autodetecting tools:&lt;/strong&gt; Currently you choose between ty and basedpyright
manually. It would be useful to detect the appropriate LSP server automatically
based on project configuration or installed tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;rass support:&lt;/strong&gt; The
&lt;a href=&quot;https://github.com/joaotavora/rassumfrassum&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rassumfrassum&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; project (an LSP
multiplexer by Eglot’s author) could integrate with eglot-python-preset to run
multiple servers like ty and Ruff simultaneously. rass might eventually handle
autodetection itself, or it could be done on the elisp side. Either way, better
integration with rass may be worth exploring.&lt;/p&gt;
&lt;h2&gt;Links&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/mwolson/eglot-python-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;GitHub repository&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://melpa.org/#/eglot-python-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;MELPA package&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mwolson.org/blog/2025-12-17-ty-a-fast-python-type-checker-and-lsp-for-emacs/&quot;&gt;ty: A Fast Python Type Checker and LSP for Emacs&lt;/a&gt;
(earlier post)&lt;/li&gt;
&lt;/ul&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item><item><title>ty: A Fast Python Type Checker and LSP for Emacs</title><link>https://mwolson.org/blog/2025-12-17-ty-a-fast-python-type-checker-and-lsp-for-emacs/</link><guid isPermaLink="true">https://mwolson.org/blog/2025-12-17-ty-a-fast-python-type-checker-and-lsp-for-emacs/</guid><description>Fast Python type checking and LSP with Eglot and Astral&apos;s ty in Emacs.</description><pubDate>Wed, 17 Dec 2025 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#introduction&quot;&gt;Introduction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#setting-up-ty-with-eglot&quot;&gt;Setting Up ty with Eglot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-multi-lsp-question&quot;&gt;The Multi-LSP Question&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#how-other-editors-handle-this&quot;&gt;How Other Editors Handle This&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#eglots-approach&quot;&gt;Eglot’s Approach&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#aside---is-an-external-multiplexer-needed&quot;&gt;Aside - Is an External Multiplexer Needed?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#current-status&quot;&gt;Current Status&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Recently &lt;a href=&quot;https://astral.sh/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Astral&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; (the team behind
&lt;a href=&quot;https://github.com/astral-sh/ruff&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ruff&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; and
&lt;a href=&quot;https://github.com/astral-sh/uv&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;uv&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;) announced the beta release of
&lt;a href=&quot;https://github.com/astral-sh/ty&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;ty&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, an extremely fast Python type checker and
language server written in Rust.&lt;/p&gt;
&lt;p&gt;The performance numbers are impressive. Without caching, ty is consistently
10-60x faster than mypy and Pyright. In an editor context, the gap is even more
dramatic: after editing a load-bearing file in the PyTorch repository, ty
recomputes diagnostics in 4.7ms, which is 80x faster than Pyright (386ms) and
500x faster than Pyrefly (2.38s).&lt;/p&gt;
&lt;p&gt;Beyond raw speed, ty includes some sophisticated type system features like
first-class intersection types, advanced type narrowing, and best-in-class
diagnostic messages inspired by Rust’s compiler. The diagnostics can pull
context from multiple files to explain not just what’s wrong, but why.&lt;/p&gt;
&lt;h2&gt;Setting Up ty with Eglot&lt;/h2&gt;
&lt;p&gt;Since ty implements the Language Server Protocol, it’s straightforward to use
with Emacs. Here’s my configuration using Eglot, the built-in LSP client:&lt;/p&gt;
&lt;aside aria-label=&quot;Update (Jan 11 2026)&quot; class=&quot;admonition&quot; data-admonition-type=&quot;note&quot;&gt;&lt;p class=&quot;admonition-title&quot; aria-hidden=&quot;true&quot;&gt;Update (Jan 11 2026)&lt;/p&gt;&lt;div class=&quot;admonition-content&quot;&gt;&lt;p&gt;This is now more easily accomplished with my
&lt;a href=&quot;https://github.com/mwolson/eglot-python-preset&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;eglot-python-preset&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; package,
which is available on MELPA and has support for inline PEP-723 dependencies.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-project-find-python-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt;dir&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;when-let&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;root &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;locate-dominating-file&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; dir &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;pyproject.toml&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;cons&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;python-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; root&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-eval-after-load&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;cl-defmethod&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; project-root &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;project &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;head python-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;cdr&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;project-find-functions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-project-find-python-project&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;auto-mode-alist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/uv&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.lock&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; toml-ts-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;major-mode-remap-alist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;python-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; python-ts-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;eglot-server-programs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;             &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;python-ts-mode python-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;               .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;ty&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;python-ts-mode-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;eglot-ensure&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;project-find-functions&lt;/code&gt; hook ensures that Eglot starts ty from the correct
directory in monorepos, picking up the right paths for code references. This is
particularly important when you have multiple Python projects with different
virtual environments.&lt;/p&gt;
&lt;p&gt;If you’re using uv to manage your tools, you can use &lt;code&gt;uvx&lt;/code&gt; instead:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;eglot-server-programs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;             &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;python-ts-mode python-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;               .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;uvx&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;ty&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;The Multi-LSP Question&lt;/h2&gt;
&lt;p&gt;A question that sometimes comes up in the Emacs community is whether multi-LSP
support is needed. For Python specifically, you might want both a type checker
(ty, Pyright) and a linter/formatter (Ruff) running simultaneously on the same
file.&lt;/p&gt;
&lt;h3&gt;How Other Editors Handle This&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;VSCode&lt;/strong&gt; handles this transparently. Multiple language servers can attach to
the same file type, and the editor merges their capabilities. This is largely
invisible to users.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Neovim&lt;/strong&gt; also has built-in support for multiple LSP clients per buffer. You
can attach as many language servers as you want to a buffer, and Neovim will
aggregate their diagnostics, completions, and other features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zed&lt;/strong&gt; has built-in support for multiple language servers per language. You can
enable or disable servers in your settings using a simple array syntax, and Zed
already includes ty as a built-in option alongside basedpyright, Pyright, Ruff,
and PyLSP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;lsp-mode&lt;/strong&gt; (for Emacs) supports running multiple servers for the same file
type. You can register a server with the &lt;code&gt;:add-on? t&lt;/code&gt; flag to start it in
parallel with other servers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;OpenCode&lt;/strong&gt; (the AI coding agent) also handles multiple LSP servers natively.
When a file is opened, OpenCode checks the file extension against all enabled
LSP servers and starts each applicable one. Diagnostics from all servers are
aggregated into a single collection, which is then provided to the LLM as
context. The implementation is straightforward: each server runs in its own
process, and diagnostics are merged by file path.&lt;/p&gt;
&lt;h3&gt;Eglot’s Approach&lt;/h3&gt;
&lt;p&gt;Eglot currently doesn’t support multiple language servers per buffer out of the
box. The primary solution being developed by Eglot’s author is
&lt;a href=&quot;https://github.com/joaotavora/rassumfrassum&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rassumfrassum&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, an external LSP
multiplexer that presents multiple LSP servers as a single server to the client.&lt;/p&gt;
&lt;p&gt;rassumfrassum works by spawning multiple LSP server subprocesses and merging
their capabilities, diagnostics, and responses. For Python with ty and Ruff, you
can run it directly from the command line:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; ty&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; server&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #E0AF68);--shiki-dark:#E0AF68;--shiki-light:#40A02B&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; ruff&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; server&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then configure Eglot to use that command:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-to-list&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;eglot-server-programs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;             &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;python-ts-mode python-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;               .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;rass&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;ty&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;--&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;ruff&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;server&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the ty + Ruff use case specifically: these tools are complementary, not
overlapping. ty is a type checker (replacing mypy/Pyright), while Ruff is a
linter and formatter (replacing Flake8/Black/isort). If you want both type
checking and linting/formatting, you’d need both servers running. That said, ty
does include some features that overlap with linting, like dead code elimination
and unused dependency detection, so the boundary may blur somewhat as ty
matures.&lt;/p&gt;
&lt;h3&gt;Aside - Is an External Multiplexer Needed?&lt;/h3&gt;
&lt;p&gt;It’s worth asking whether an external tool like rassumfrassum is the right
approach when most other editors have built-in multi-LSP support.&lt;/p&gt;
&lt;p&gt;While this may keep Eglot’s codebase simpler and promote reusability, it’s also
very much not aligned with what other popular editors are doing, as we’ve shown
earlier.&lt;/p&gt;
&lt;p&gt;One could argue that rass makes it easier to customize server combinations
per-project via its Python preset files. But this is really just trading one
config format for another - Eglot already supports per-project configuration via
&lt;code&gt;.dir-locals.el&lt;/code&gt;. I also like the competing idea of autodetecting what’s needed:
my &lt;a href=&quot;https://github.com/mwolson/emacs-shared&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;emacs-shared&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; configuration has
this, albeit just for JS/TS projects currently.&lt;/p&gt;
&lt;aside aria-label=&quot;Update (Jan 11 2026)&quot; class=&quot;admonition&quot; data-admonition-type=&quot;note&quot;&gt;&lt;p class=&quot;admonition-title&quot; aria-hidden=&quot;true&quot;&gt;Update (Jan 11 2026)&lt;/p&gt;&lt;div class=&quot;admonition-content&quot;&gt;&lt;p&gt;I discussed
&lt;a href=&quot;https://github.com/joaotavora/rassumfrassum#performance&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;rass’s performance&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
with its author, who noted that Python is not currently a bottleneck in
practice, as rass spends roughly 90% of its time waiting for I/O from the
underlying LSP servers, with only 10% on actual processing. The documentation
has also been improved to mention how to use rass without presets.&lt;/p&gt;&lt;p&gt;It’s also possible in theory to have rass perform autodetection of which tool to
use for each workspace. That might not be implemented yet at the time of this
update.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;&lt;strong&gt;Further pain points with rass:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Extra complexity for users (another tool to install and configure)&lt;/li&gt;
&lt;li&gt;Potential performance overhead from the proxy layer, especially given the
implementation in Python&lt;/li&gt;
&lt;li&gt;I’d expect multi-LSP to “just work” like it does in other editors&lt;/li&gt;
&lt;li&gt;lsp-mode already handles this natively&lt;/li&gt;
&lt;li&gt;The documentation emphasizes Python config files (&lt;code&gt;.py&lt;/code&gt; presets) over
command-line arguments, which makes the tool feel more complex than it needs
to be for simple use cases&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One caveat: the approach of splitting up multiple LSP servers using a &lt;code&gt;--&lt;/code&gt;
separator seems reasonably ergonomic to use with &lt;code&gt;eglot-server-programs&lt;/code&gt;, but I
wish that it were brought forward a bit more in the docs.&lt;/p&gt;
&lt;p&gt;I think I have to conclude that if rass continues to be the way forward with
Eglot, I may well end up switching to lsp-mode in the future. This may
especially be the case for JS/TS projects, which will likely have fun
combinations of &lt;code&gt;eslint&lt;/code&gt;, &lt;code&gt;oxlint&lt;/code&gt;, and &lt;code&gt;biome&lt;/code&gt; for a while due to the evolving
nature of those tools and the performance vs functionality trade-offs.&lt;/p&gt;
&lt;h2&gt;Current Status&lt;/h2&gt;
&lt;p&gt;ty is in beta, so some LSP features may not be fully implemented yet. The
&lt;a href=&quot;https://docs.astral.sh/ty/features/language-server/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;official docs&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; list
supported capabilities including Go to Definition, Symbol Rename, Auto-Complete,
Auto-Import, Semantic Syntax Highlighting, and Inlay Hints.&lt;/p&gt;
&lt;p&gt;A patch to add ty to Eglot’s default server list has already been submitted by
Steve Purcell (the MELPA maintainer), so it should be included in a future Emacs
release.&lt;/p&gt;
&lt;p&gt;If you’re doing Python development in Emacs, ty is worth trying out. The speed
improvements alone make it compelling, and the diagnostic quality is excellent.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item><item><title>My Emacs AI Setup</title><link>https://mwolson.org/blog/2025-12-03-my-emacs-ai-setup/</link><guid isPermaLink="true">https://mwolson.org/blog/2025-12-03-my-emacs-ai-setup/</guid><description>How I use AI tools in Emacs: minuet for completions, gptel-fn-complete for rewrites, OpenCode for most else.</description><pubDate>Wed, 03 Dec 2025 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;p&gt;I’ve been evolving my Emacs AI workflow over the past year since releasing
&lt;code&gt;gptel-fn-complete&lt;/code&gt;, and I’m happy with where it’s currently landed. This post
describes most of the AI tools that I use with Emacs and how they fit together.&lt;/p&gt;
&lt;p&gt;A high-level overview:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I pare down and simplify
&lt;a href=&quot;https://github.com/milanglacier/minuet-ai.el&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;minuet&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;’s functionality quite a
bit, and control which files and kinds of buffers it can act on.&lt;/li&gt;
&lt;li&gt;I tend to use &lt;a href=&quot;https://github.com/karthink/gptel&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; as the source of
truth for which providers and models to use, as well as any common arguments
like temperature or amount of thinking.&lt;/li&gt;
&lt;li&gt;I tend to use &lt;a href=&quot;https://opencode.ai&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OpenCode&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; outside of Emacs for most AI
work, and then rely on &lt;code&gt;auto-revert-mode&lt;/code&gt; (as implicitly enabled by
&lt;a href=&quot;https://magit.vc&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Magit&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;) to have Emacs catch up and review the changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Inline completions with minuet&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/milanglacier/minuet-ai.el&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;minuet&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; provides automatic inline
AI auto-completions in Emacs. I like that Minuet includes just the right amount
of buffer context with the completions API, which improves the quality of the
results.&lt;/p&gt;
&lt;p&gt;By default, it shows multiple suggestions that you can cycle through, but I
prefer the Cursor-style approach of showing just one suggestion at a time. This
keeps the experience very simple and low-friction.&lt;/p&gt;
&lt;p&gt;I’ll explain how to achieve that over the next few sections.&lt;/p&gt;
&lt;h3&gt;Basic configuration&lt;/h3&gt;
&lt;p&gt;After installing &lt;code&gt;minuet&lt;/code&gt; and &lt;code&gt;gptel&lt;/code&gt; from MELPA, I configure Minuet like so:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-eval-after-load&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;minuet&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setopt minuet-add-single-line-entry &lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt;nil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;          minuet-auto-suggestion-debounce-delay &lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;0.3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;          minuet-n-completions &lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set minuet-active-mode-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c C-c&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-accept-suggestion&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set minuet-active-mode-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c C-n&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-next-suggestion&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set minuet-active-mode-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c C-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-previous-suggestion&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set minuet-active-mode-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-g&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-dismiss-suggestion&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set minuet-active-mode-map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;&amp;lt;tab&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-accept-suggestion-line&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The settings are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;minuet-add-single-line-entry&lt;/code&gt;: Don’t clutter the completion menu too much
with extra single-line versions of the suggestions&lt;/li&gt;
&lt;li&gt;&lt;code&gt;minuet-auto-suggestion-debounce-delay&lt;/code&gt;: How long to wait before requesting a
suggestion&lt;/li&gt;
&lt;li&gt;&lt;code&gt;minuet-n-completions&lt;/code&gt;: Set to &lt;code&gt;1&lt;/code&gt; for Cursor-style single suggestions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once a code complete suggestion is presented, the following keys are available,
in order of importance:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;TAB&lt;/kbd&gt;: Complete just the first line of the suggestion&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-c C-c&lt;/kbd&gt;: Insert the entire multi-line suggestion&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-g&lt;/kbd&gt;: Remove the suggestion&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-n&lt;/kbd&gt; and &lt;kbd&gt;C-p&lt;/kbd&gt;: Cycle through the next or previous
suggestions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ll now describe a few other customizations that I think make Minuet a bit more
ergonomic.&lt;/p&gt;
&lt;h3&gt;Blocking some suggestions within buffers&lt;/h3&gt;
&lt;p&gt;I make sure that suggestions only happen at the end of non-empty lines, to avoid
having them trigger in cases where I’m reading through the file quickly or
editing existing code. It’s also important to not attempt suggestions when the
buffer is read-only, since that will never be helpful.&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-minuet-block-suggestions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Return nil if we should show suggestions, t (blocked) otherwise.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Criteria:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;- File must be writable&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;- Cursor must not be at beginning of line&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;- Cursor must be at the end of line (ignoring whitespace).&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; buffer-read-only&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;            (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;bolp&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;            (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;looking-at-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;s&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;*$&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-eval-after-load&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;minuet&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-auto-suggestion-block-predicates&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;            #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-minuet-block-suggestions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; -100&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Enabling Minuet at file-level&lt;/h3&gt;
&lt;p&gt;I enable Minuet’s auto-suggestion mode in programming buffers, with some
exclusions:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; kill switch for minuet functionality, currently allowed:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defvar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; my-minuet-auto-suggest-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; my shared Emacs settings have this (disabled on every file by default):&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defvar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; my-minuet-exclude-file-regexps&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.*&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; my personal configuration has something like this:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;setq my-minuet-exclude-file-regexps &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.emacs.d/&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.gnupg/&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.ssh/&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-minuet-exclude&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;let* &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;filename &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;buffer-file-name&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;or&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; filename&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;        (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;when-let*&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;lst my-minuet-exclude-file-regexps&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;          (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;string-match-p&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;           (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;string-join&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;mapcar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;##&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;concat &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;(?:&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; %&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; lst&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;\\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;           filename&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-minuet-maybe-turn-on-auto-suggest&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;when&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-minuet-auto-suggest-p &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-minuet-exclude&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;minuet-auto-suggestion-mode &lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;prog-mode-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-minuet-maybe-turn-on-auto-suggest&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll want to customize &lt;code&gt;my-minuet-exclude-file-regexps&lt;/code&gt; to make sure any other
sensitive directories are excluded from having their buffers sent to AI.&lt;/p&gt;
&lt;h3&gt;Provider configuration&lt;/h3&gt;
&lt;p&gt;I sync minuet’s provider settings from my
&lt;a href=&quot;https://github.com/karthink/gptel&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; configuration, which lets me use the
same API keys and model settings across both tools. This part is a bit complex,
but I switch between models often enough that it ended up being useful to have.&lt;/p&gt;
&lt;p&gt;The code for this is available separately on my
&lt;a href=&quot;https://mwolson.org/guides/emacs-ai-setup/&quot;&gt;Emacs AI Setup Guide&lt;/a&gt; since it was getting a bit too
long for a blog post. The gist is that I only configure API keys once in my
&lt;code&gt;auth-source&lt;/code&gt; file, and both gptel and minuet can use them.&lt;/p&gt;
&lt;p&gt;For AI models within Emacs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I currently use Kimi K2 for Minuet suggestions.&lt;/li&gt;
&lt;li&gt;I use Claude Opus 4.5 for one-off gptel prompts and &lt;code&gt;gptel-fn-complete&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I currently use &lt;a href=&quot;https://opencode.ai/docs/zen&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OpenCode Zen&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; exclusively, for
several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They have a tasteful selection of models.&lt;/li&gt;
&lt;li&gt;It’s easy to switch between different models with the same API credit pool,
which can be configured to automatically reload when below a threshold.&lt;/li&gt;
&lt;li&gt;When they run a model, it’s almost certain to be stable, unlike my prior
experience with some Openrouter providers.&lt;/li&gt;
&lt;li&gt;They have models that have been no-cost for a while, and they recently started
offering GPT 5.1 (one of my primary writing, coding, and task-following
models) at a lower price than OpenAI does.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Keybinds&lt;/h3&gt;
&lt;p&gt;I have the following other keybinds to manually trigger &lt;code&gt;minuet&lt;/code&gt; or
&lt;code&gt;gptel-fn-complete&lt;/code&gt;, in addition to some other useful &lt;code&gt;xref&lt;/code&gt; bindings:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defvar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;let &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;make-sparse-keymap&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;minuet-show-suggestion&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;f&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;gptel-fn-complete&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-find-definitions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-go-back&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-find-references&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-set map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;RET&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;embark-act&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;    map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;My key customizations for AI and xref.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-global-set &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-global-set &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c C-.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-global-set &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-x .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;keymap-global-set &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-x C-.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since these get called somewhat frequently, I like to bind a few adjacent keys
to the same thing, in case I miss a keypress or hold down &lt;kbd&gt;Ctrl&lt;/kbd&gt; for
too long.&lt;/p&gt;
&lt;h2&gt;Completing functions with gptel-fn-complete&lt;/h2&gt;
&lt;p&gt;Earlier in the year I wrote
&lt;a href=&quot;https://github.com/mwolson/gptel-fn-complete&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel-fn-complete&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;. It uses
&lt;a href=&quot;https://github.com/karthink/gptel&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; under the hood, and is helpful for
completing entire functions or regions. It makes a reasonable attempt to
automatically find the bounds of the function at point, and if you’re using a
treesit-compatible mode, it will always succeed at that. This was nice to pair
with local AI, since the context sent to the AI did not have to be very large.&lt;/p&gt;
&lt;p&gt;Now that the tooling and remote AI models have improved to such a large degree,
I find myself using OpenCode almost exclusively instead.&lt;/p&gt;
&lt;h2&gt;Agentic workflows with OpenCode&lt;/h2&gt;
&lt;p&gt;I use &lt;a href=&quot;https://opencode.ai/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OpenCode&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; for larger tasks that benefit from an
agentic approach, such as exploring codebases, making multi-file changes, or
debugging complex issues.&lt;/p&gt;
&lt;p&gt;OpenCode runs in the terminal and has access to tools for reading files,
searching code, and making edits. I typically run it in a
&lt;a href=&quot;https://ghostty.org/docs&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ghostty&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; terminal rather than inside Emacs; if I
change my mind about that, I’d likely pick up
&lt;a href=&quot;https://github.com/xenodium/agent-shell&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;agent-shell&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; to manage OpenCode.&lt;/p&gt;
&lt;p&gt;One helpful (and perhaps unexpected) side effect of using
&lt;a href=&quot;https://magit.vc&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Magit&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; is that it has an automatically activated major mode
named &lt;code&gt;magit-auto-revert-mode&lt;/code&gt; that automatically turns on &lt;code&gt;auto-revert-mode&lt;/code&gt;
when an open Emacs buffer is tracked using Git.&lt;/p&gt;
&lt;p&gt;In practice this means that if OpenCode is making changes, Emacs will pick those
up and refresh the buffer contents automatically. This ends up being a great
workflow for me, since I can review in Emacs what the AI model did, without
having to bring in any specialized Emacs modes to help. I especially don’t want
to review diffs in any tool other than Magit.&lt;/p&gt;
&lt;h2&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;This three-layer approach covers my AI needs well:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;minuet&lt;/strong&gt; for seamless inline completions while typing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;gptel-fn-complete&lt;/strong&gt; for quick, targeted code actions at point&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OpenCode&lt;/strong&gt; for almost every kind of other task&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In case any of the above doesn’t work as-is (which is quite possible), it may
also be helpful to refer to my
&lt;a href=&quot;https://github.com/mwolson/emacs-shared&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;shared Emacs settings&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; repo.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>ai</category><category>emacs</category><author>Mike Olson</author></item><item><title>Automatic Arrow Characters in Emacs</title><link>https://mwolson.org/blog/2025-11-23-automatic-arrow-characters-in-emacs/</link><guid isPermaLink="true">https://mwolson.org/blog/2025-11-23-automatic-arrow-characters-in-emacs/</guid><description>How to set up automatic conversion of &quot;-&quot; and &quot;&gt;&quot; to &quot;→&quot; in Emacs.</description><pubDate>Sun, 23 Nov 2025 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;h2&gt;Contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#intro&quot;&gt;Intro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#quail-setup&quot;&gt;Quail Setup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#markdown-mode-activation&quot;&gt;Markdown Mode Activation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#handling-indirect-buffers&quot;&gt;Handling Indirect Buffers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#why-not-use-abbrevs&quot;&gt;Why Not Use Abbrevs?&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#alternative-abbrev-mode&quot;&gt;Alternative: abbrev-mode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#alternative-auto-activating-snippets&quot;&gt;Alternative: auto-activating-snippets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Intro&lt;/h2&gt;
&lt;p&gt;While adding technical writing to &lt;a href=&quot;https://mwolson.org/&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;my new website&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; using
Emacs, I realized I was used to working with software that automatically
converted the character &lt;code&gt;-&lt;/code&gt; followed by &lt;code&gt;&amp;gt;&lt;/code&gt; to the Unicode arrow character &lt;code&gt;→&lt;/code&gt;.
I decided to add the same behavior to my Emacs setup, and I’d like to describe
how I did it.&lt;/p&gt;
&lt;p&gt;Emacs has a library called &lt;code&gt;quail&lt;/code&gt; that allows defining custom character
mappings. If the left side of the mapping has multiple characters, and you type
them consecutively, Emacs replaces them with the right side.&lt;/p&gt;
&lt;h2&gt;Quail Setup&lt;/h2&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;quail-define-package&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Arrows&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;UTF-8&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;→&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Arrow input mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; t t &lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt;nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; nil&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;quail-define-rules&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;→&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a new input method called “Arrows” that converts &lt;code&gt;-&amp;gt;&lt;/code&gt; to &lt;code&gt;→&lt;/code&gt; as you
type. I have to caveat that the arguments to &lt;code&gt;quail-define-package&lt;/code&gt; are a bit
unergonomic, and having &lt;code&gt;quail-define-rules&lt;/code&gt; not refer to the “Arrows” method
explicitly is a bit odd (it applies its rules to the most recently defined quail
input method/package), but it gets the job done.&lt;/p&gt;
&lt;h2&gt;Markdown Mode Activation&lt;/h2&gt;
&lt;p&gt;To use this input method specifically in Markdown mode, I have the following:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-turn-on-arrow-input&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Turn on arrow input mode.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #9D7CD8);--shiki-dark:#9D7CD8;--shiki-dark-font-style:italic;--shiki-light:#8839EF;--shiki-light-font-style:inherit&quot;&gt;interactive&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;set-input-method&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;Arrows&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;markdown-mode-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-turn-on-arrow-input&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; t&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Handling Indirect Buffers&lt;/h2&gt;
&lt;p&gt;The above is already enough to accomplish what’s needed, but I’ll explain one
other edge case that I cover for Markdown specifically.&lt;/p&gt;
&lt;p&gt;I use &lt;code&gt;(setopt markdown-fontify-code-blocks-natively t)&lt;/code&gt; to fontify code blocks
within Markdown buffers using their native major modes. Previously I used
&lt;code&gt;poly-markdown-mode&lt;/code&gt; for that, but the built-in &lt;code&gt;markdown-mode&lt;/code&gt; approach seems
more robust lately.&lt;/p&gt;
&lt;p&gt;One precaution that I ported over from &lt;code&gt;polymode&lt;/code&gt; is the ability to exclude
certain hooks from running in the indirect buffers associated with those code
blocks, in case they cause slowdowns or problems. I’ll present those here:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-in-indirect-md-buffer-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Return non-nil if the current buffer is an indirect buffer created from a Markdown buffer.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;when-let*&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;buf &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;buffer-base-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;buffer-live-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; buf&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;         (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-current-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; buf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;           (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;derived-mode-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;markdown-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-inhibit-in-indirect-md-buffers&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt;orig-fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#D20F39, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#D20F39&quot;&gt; &amp;amp;rest&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt; args&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Don&apos;t run ORIG-FUN (with ARGS) in indirect markdown buffers.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Use this to advise functions that could be problematic.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;unless&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-in-indirect-md-buffer-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;apply&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; orig-fun args&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-around-advice&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt;fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#E64553, #E0AF68);--shiki-dark:#E0AF68;--shiki-dark-font-style:inherit;--shiki-light:#E64553;--shiki-light-font-style:italic&quot;&gt; advice&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Apply around ADVICE to FUN.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;If FUN is a list, apply ADVICE to each element of it.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;cond&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;listp&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;         (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;dolist&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;el fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-around-advice el advice&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;        ((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;and&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;symbolp&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;              (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;not&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;advice-member-p&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; advice fun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;         (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;advice-add&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; fun &lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#8839EF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;around advice&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I then ensure that the &lt;code&gt;Arrows&lt;/code&gt; input method only activates in the main Markdown
buffers, not indirect ones:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;my-around-advice &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;#&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-turn-on-arrow-input&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;                  #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-inhibit-in-indirect-md-buffers&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This implementation, along with quite a few other customizations, is also
available in my &lt;a href=&quot;https://github.com/mwolson/emacs-shared&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;shared .emacs setup&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Some inspiration was taken from
&lt;a href=&quot;https://www.masteringemacs.org/article/inserting-emoji-input-methods&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;this Mastering Emacs post&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
which describes how to make your own emoji input method with Quail.&lt;/p&gt;
&lt;h2&gt;Why Not Use Abbrevs?&lt;/h2&gt;
&lt;p&gt;After publishing this post, someone suggested using a local abbrev instead,
which would remove the need to inhibit activation in non-Markdown buffers. This
seemed like a reasonable alternative, so I investigated.&lt;/p&gt;
&lt;h3&gt;Alternative: abbrev-mode&lt;/h3&gt;
&lt;p&gt;It turns out that abbrevs aren’t quite as ergonomic for this use case. The
reason comes down to how Emacs categorizes characters into
&lt;a href=&quot;https://www.gnu.org/software/emacs/manual/html_node/elisp/Syntax-Class-Table.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;syntax classes&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;.
Abbrevs expand when you type a non-word-constituent character after a sequence
of word-constituent characters. The problem is that &lt;code&gt;-&lt;/code&gt; is typically a
punctuation character in most modes, not a word constituent. This means:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You can’t define an abbrev &lt;code&gt;-&amp;gt;&lt;/code&gt; that works normally, because &lt;code&gt;-&lt;/code&gt; itself would
trigger expansion of whatever word came before it.&lt;/li&gt;
&lt;li&gt;To make abbrevs work, you’d need to either modify the syntax table to make
&lt;code&gt;-&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; word constituents, or implement a custom &lt;code&gt;abbrev-expand-function&lt;/code&gt;
to handle symbol syntax.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you wanted to pursue the abbrev approach anyway, here’s a working example
that modifies the syntax table:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;define-abbrev&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; gfm-mode-abbrev-table &lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;→&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-markdown-abbrev-setup&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Make &lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; and &lt;/span&gt;&lt;span style=&quot;color:light-dark(#EA76CB, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#EA76CB&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt; word constituents so that `-&amp;gt;` abbrev works.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;modify-syntax-entry&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;w&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;modify-syntax-entry&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; ?&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;w&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;abbrev-mode&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;add-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;markdown-mode-hook&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;my-markdown-abbrev-setup&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: the &lt;code&gt;gfm-mode-abbrev-table&lt;/code&gt; is specific to &lt;code&gt;gfm-mode&lt;/code&gt;. If you use plain
&lt;code&gt;markdown-mode&lt;/code&gt;, substitute &lt;code&gt;markdown-mode-abbrev-table&lt;/code&gt; instead.&lt;/p&gt;
&lt;p&gt;However, this approach has several trade-offs compared to Quail:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Changing the syntax class of &lt;code&gt;-&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt; affects word motion commands (&lt;code&gt;M-f&lt;/code&gt;,
&lt;code&gt;M-b&lt;/code&gt;), so &lt;code&gt;foo-bar&lt;/code&gt; would now be treated as a single word rather than two.&lt;/li&gt;
&lt;li&gt;You must type a space (or other non-word character) after &lt;code&gt;-&amp;gt;&lt;/code&gt; to trigger
expansion, rather than having it expand immediately as you type.&lt;/li&gt;
&lt;li&gt;There’s no visual feedback while typing. Quail highlights the first character
when a potential expansion is in progress, but abbrev-mode has no equivalent
preview feature.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Quail input method approach avoids these trade-offs. It operates at a
different level, matching literal character sequences regardless of syntax
class, and expands immediately as you type - which matches the Notion-like
behavior I was after.&lt;/p&gt;
&lt;h3&gt;Alternative: auto-activating-snippets&lt;/h3&gt;
&lt;p&gt;If you’d prefer a package-based solution that behaves more like Quail,
&lt;a href=&quot;https://github.com/ymarco/auto-activating-snippets&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;auto-activating-snippets&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;
(available on MELPA as &lt;code&gt;aas&lt;/code&gt;) expands snippets as you type without requiring a
trigger key.&lt;/p&gt;
&lt;p&gt;This gives you immediate expansion like Quail, without syntax table
modifications. The main difference from Quail is that &lt;code&gt;aas&lt;/code&gt; doesn’t provide
visual feedback during typing - you won’t see highlighting on the first
character when an expansion is possible.&lt;/p&gt;
&lt;p&gt;Also at the time of this writing, it hasn’t been updated since 2023-03-03 and
may have byte-compilation warnings.&lt;/p&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item><item><title>gptel-manual-complete: AI function completion in Emacs</title><link>https://mwolson.org/blog/2025-05-19-gptel-manual-complete/</link><guid isPermaLink="true">https://mwolson.org/blog/2025-05-19-gptel-manual-complete/</guid><description>Complete entire functions in Emacs using AI with gptel-manual-complete.</description><pubDate>Mon, 19 May 2025 00:00:00 GMT</pubDate><content:encoded>
    &lt;div class=&quot;mwo-wrapper&quot; style=&quot;line-height: 1.6; font-family: -apple-system, BlinkMacSystemFont, &apos;Segoe UI&apos;, Roboto, sans-serif;&quot;&gt;
      &lt;p&gt;This is an example of how the existing &lt;code&gt;gptel-rewrite.el&lt;/code&gt; file can be used to
perform completion on an entire function, replacing what’s already written so
far in that function.&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;To use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href=&quot;https://github.com/karthink/gptel&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;, configure it, and provide
the appropriate API keys&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href=&quot;https://github.com/mwolson/gptel-fn-complete&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;gptel-fn-complete&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now choose which key you’d like to bind it to. I typically add something like
this to my Emacs config:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defvar&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #C0CAF5);--shiki-dark:#C0CAF5;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;let &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;make-sparse-keymap&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;define-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;kbd&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;c&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;gptel-manual-complete&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;define-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;kbd&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-find-definitions&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;define-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;kbd&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-go-back&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;define-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; map &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;kbd&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt; #&apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#4C4F69&quot;&gt;xref-find-references&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;    map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;My key customizations for AI and xref.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;global-set-key&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;kbd&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;C-c .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-xref-map&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Restart Emacs&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Usage&lt;/h2&gt;
&lt;p&gt;If you’ve used the above keybinds, they work like this (the only with AI is the
first one):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;kbd&gt;C-c . c&lt;/kbd&gt; to complete the code at point using Claude AI; if you have
a comment near the end, that will better inform the completion&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-c . .&lt;/kbd&gt; to visit the definition of the thing at point&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-c . ,&lt;/kbd&gt; to return to the original point after visiting something&lt;/li&gt;
&lt;li&gt;&lt;kbd&gt;C-c . /&lt;/kbd&gt; to find references to the thing at point&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Example&lt;/h2&gt;
&lt;p&gt;When I write this code in a &lt;code&gt;sample.el&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-code&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;AI should not modify this.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Sample 1&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-say-hello&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #A8A29E);--shiki-dark:#A8A29E;--shiki-dark-font-style:italic;--shiki-light:#7C7F93;--shiki-light-font-style:italic&quot;&gt;;; print a welcoming message in a window off to the right&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-other-code&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;AI should not modify this either.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;message&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Sample 2&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Move the cursor into the body of &lt;code&gt;my-hello&lt;/code&gt; and hit &lt;kbd&gt;C-c . c&lt;/kbd&gt; then
gptel will rewrite that &lt;code&gt;my-hello&lt;/code&gt; function to something like this, without
touching the other functions or deleting lines around it (results may vary, I
used Claude 3.5 Sonnet in this example):&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes tokyo-night catppuccin-latte&quot; style=&quot;background-color:light-dark(#eff1f5, #1a1b26);--shiki-dark-bg:#1a1b26;--shiki-light-bg:#eff1f5;color:light-dark(#4c4f69, #a9b1d6);--shiki-dark:#a9b1d6;--shiki-light:#4c4f69;overflow-x:auto&quot; tabindex=&quot;0&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#8839EF, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#8839EF&quot;&gt;defun&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; my-hello&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt; ()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;  &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Print a welcoming message in a window off to the right.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;  (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;let &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;buf &lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;get-buffer-create&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;*Hello*&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;with-current-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; buf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;      (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;erase-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;      (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;insert&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt; &quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Welcome to Emacs!&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;\&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #BB9AF7);--shiki-dark:#BB9AF7;--shiki-light:#40A02B&quot;&gt;n&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #9ECE6A);--shiki-dark:#9ECE6A;--shiki-light:#40A02B&quot;&gt;Have a productive session.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#40A02B, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#40A02B&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;    (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;display-buffer&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; buf&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#7C7F93&quot;&gt;                    &apos;&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;((&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;display-buffer-reuse-window&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;                       display-buffer-in-side-window&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;                      (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt;side &lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:light-dark(#4C4F69, #A9B1D6);--shiki-dark:#A9B1D6;--shiki-light:#4C4F69&quot;&gt; right&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;                      (&lt;/span&gt;&lt;span style=&quot;color:light-dark(#1E66F5, #0DB9D7);--shiki-dark:#0DB9D7;--shiki-dark-font-style:inherit;--shiki-light:#1E66F5;--shiki-light-font-style:italic&quot;&gt;window-width&lt;/span&gt;&lt;span style=&quot;color:light-dark(#179299, #89DDFF);--shiki-dark:#89DDFF;--shiki-light:#179299&quot;&gt; .&lt;/span&gt;&lt;span style=&quot;color:light-dark(#FE640B, #FF9E64);--shiki-dark:#FF9E64;--shiki-light:#FE640B&quot;&gt; 40&lt;/span&gt;&lt;span style=&quot;color:light-dark(#7C7F93, #9ABDF5);--shiki-dark:#9ABDF5;--shiki-light:#7C7F93&quot;&gt;)))))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From here, you can use the standard &lt;code&gt;gptel-rewrite&lt;/code&gt; keys like &lt;kbd&gt;C-c C-a&lt;/kbd&gt;
on that code to accept it and remove the overlay on it.&lt;/p&gt;
&lt;p&gt;Note that the function must have balanced parentheses, otherwise the code will
throw an error. This is to make it easier to locate the beginning and end of the
function to send to gptel’s context.&lt;/p&gt;
&lt;h2&gt;Inspiration&lt;/h2&gt;
&lt;p&gt;After adding a function to gptel’s context, I was using &lt;code&gt;gptel-rewrite&lt;/code&gt; and
accidentally hit Enter twice. This resulted in just the basic “Rewrite: ” text
being sent, and to my surprise that was very effective at having Claude fix the
problem I was going to ask about.&lt;/p&gt;
&lt;p&gt;I decided to see if Claude could also do code completions this way, with a very
terse kind of prompt on top of the standard &lt;code&gt;gptel-rewrite&lt;/code&gt; prompt, and it turns
out that it can!&lt;/p&gt;
&lt;h2&gt;Notes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;For automatically identifying the entire current function to complete, you may
have the best luck with either Emacs Lisp or files with major modes that have
a tree-sitter grammar installed, as otherwise we have to guess. In general it
should err on the side of sending too little rather than too much.&lt;/li&gt;
&lt;li&gt;My Emacs setup is available at &lt;a href=&quot;https://github.com/mwolson/emacs-shared&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/mwolson/emacs-shared&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt; which
has this and other features
&lt;ul&gt;
&lt;li&gt;Note that the install doc might take a while to get through, and may have
opinionated settings&lt;/li&gt;
&lt;li&gt;The additional AI features which have more bindings on &lt;kbd&gt;C-c .&lt;/kbd&gt; than
in the above example
&lt;a href=&quot;https://github.com/mwolson/emacs-shared/blob/master/doc/tips.md#using-ai-and-finding-definitions&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;are described here&lt;span class=&quot;extra-content&quot;&gt;↗&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
      &lt;style&gt;
      .mwo-wrapper {
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;
  --color-secondary: #a21caf;
  --color-fg-content: #1e293b;
  --color-h1: #1e293b;
  --color-h2: #1e40af;
  --color-code: #a21caf;
  --color-emph: #3b82f6;
  --color-kbd-shadows: color-mix(
    in oklab,
    oklch(21% 0.034 264.665) 10%,
    transparent
  );
  --color-card-bg: #f8fafc;
  --color-border: #e2e8f0;
  --color-admonition-note: #3b82f6;
  --color-admonition-tip: #65a30d;
  --color-admonition-important: #a855f7;
  --color-admonition-caution: #f97316;
  --color-admonition-warning: #dc2626;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  .mwo-wrapper {
    --color-primary: #a188d3;
    --color-primary-hover: #b9a3e3;
    --color-secondary: #e879f9;
    --color-fg-content: #f9fafb;
    --color-h1: #f9fafb;
    --color-h2: #60a5fa;
    --color-h3: #f472b6;
    --color-code: #f472b6;
    --color-emph: #60a5fa;
    --color-kbd-shadows: rgb(255 255 255 / 10%);
    --color-card-bg: #1f2937;
    --color-border: #374151;
    --color-admonition-note: #60a5fa;
    --color-admonition-tip: #84cc16;
    --color-admonition-important: #c084fc;
    --color-admonition-caution: #fb923c;
    --color-admonition-warning: #f87171;
    color-scheme: dark;
  }
}

.mwo-wrapper {
  color: var(--color-fg-content);

  h1 {
    color: var(--color-h1);
  }

  h2 {
    color: var(--color-h2);
  }

  h3 {
    color: var(--color-h3);
  }

  a {
    color: var(--color-primary);
  }

  a:hover {
    color: var(--color-primary-hover);
  }

  pre.astro-code {
    border-color: var(--color-border);
    background-color: var(--color-card-bg) !important;
  }

  code {
    font-variant-ligatures: none;
  }

  blockquote {
    color: var(--color-h2);
    border-color: var(--color-border);
  }

  strong,
  b {
    color: var(--color-emph);
  }

  em,
  i {
    color: var(--color-fg-content);
  }

  kbd {
    font-size: 0.8888889em;
    border-radius: 0.3125rem;
    padding-top: 0.2222222em;
    padding-inline-end: 0.4444444em;
    padding-bottom: 0.2222222em;
    padding-inline-start: 0.4444444em;
    font-weight: 500;
    font-family: inherit;
    box-shadow:
      0 0 0 1px var(--color-kbd-shadows),
      0 3px 0 var(--color-kbd-shadows);
  }

  a span.extra-content::before {
    content: &quot;\2005&quot;;
  }

  /* Admonition styles */
  aside.admonition {
    --admonition-color: var(--color-admonition-note);
    margin: 1.5em 0;
    padding: 0.75em 1em;
    border-left: 4px solid var(--admonition-color);
    background-color: color-mix(
      in srgb,
      var(--admonition-color) 8%,
      transparent
    );
    border-radius: 0 0.25em 0.25em 0;
  }

  aside.admonition[data-admonition-type=&quot;note&quot;] {
    --admonition-color: var(--color-admonition-note);
  }

  aside.admonition[data-admonition-type=&quot;tip&quot;] {
    --admonition-color: var(--color-admonition-tip);
  }

  aside.admonition[data-admonition-type=&quot;important&quot;] {
    --admonition-color: var(--color-admonition-important);
  }

  aside.admonition[data-admonition-type=&quot;caution&quot;] {
    --admonition-color: var(--color-admonition-caution);
  }

  aside.admonition[data-admonition-type=&quot;warning&quot;] {
    --admonition-color: var(--color-admonition-warning);
  }

  aside.admonition .admonition-title {
    margin: 0 0 0.5em 0;
    font-weight: 600;
    color: var(--admonition-color);
    text-transform: capitalize;
  }

  aside.admonition .admonition-content {
    margin: 0;
  }

  aside.admonition .admonition-content &gt; :first-child {
    margin-top: 0;
  }

  aside.admonition .admonition-content &gt; :last-child {
    margin-bottom: 0;
  }
}

      &lt;/style&gt;
    &lt;/div&gt;
  </content:encoded><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Mike Olson</dc:creator><category>emacs</category><author>Mike Olson</author></item></channel></rss>