<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Neovim on Clément Joly – Open-Source, Rust &amp; SQLite</title><link>https://cj.rs/tags/neovim/</link><description>Recent content in Neovim on Clément Joly – Open-Source, Rust &amp; SQLite</description><image><title>Clément Joly – Open-Source, Rust &amp; SQLite</title><url>https://cj.rs/images/open-graph-pages.jpg</url><link>https://cj.rs/images/open-graph-pages.jpg</link></image><generator>Hugo</generator><language>en</language><copyright>Clément Joly</copyright><atom:link href="https://cj.rs/tags/neovim/index.xml" rel="self" type="application/rss+xml"/><item><title>Features I want to try first in Neovim 0.12</title><link>https://cj.rs/blog/nvim0.12/</link><pubDate>Sun, 29 Mar 2026 20:06:06 +0100</pubDate><guid>https://cj.rs/blog/nvim0.12/</guid><description>Auto-completion without plugins, improved UI core, plugin management and more...</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-comment">
    <p class="alert-heading">
      💬
      
        This is a comment on
      
    </p>
    <p><a href="https://neovim.io/doc/user/news-0.12/">Neovim 0.12 News</a> (<a href="https://web.archive.org/web/20260329192230/https://neovim.io/doc/user/news/">archive</a>)</p>
  </div>



<p><a href="https://github.com/neovim/neovim/releases/tag/v0.12.0">Neovim 0.12</a> was released earlier today.
I don’t have enough time just now, but I’m sharing here the list of things from <a href="https://neovim.io/doc/user/news-0.12/">the release page</a> that I want to try first.</p>
<h2 id="whole-new-features">Whole new features</h2>
<p>Here are new features to replace some plugins and simplify my configuration:</p>
<ul>
<li>There is a new <code>'autocomplete'</code> option. When set, completion suggestions appear in insert mode, without pressing a triggering shortcut. The <code>'complete'</code> option also gains the ability to call arbitrary functions, as long as they follow the <a href="https://neovim.io/doc/user/insert/#complete-functions"><code>complete-functions</code></a> interface. These two features combined could replace the many completion plugins that came and went over the years. I certainly hope to use them to remove mini.completion from my config.<br>
More importantly, they might help the ecosystem to standardize around a common interface for completion sources. At the moment, LSP servers play this role, but a full-blown LSP server implementation in every plugin is quite heavy. For example, <a href="https://github.com/saecki/crates.nvim">crates.nvim</a> supports some completion plugins (nvim-cmp, coq.nvim) and also exposes an LSP server for cross-compatibility support. In the future, such a crate might only implement the <code>complete-functions</code> interface and it could be used as a source by either native nvim completions or plugins.</li>
<li>The other big highlight of this release is the native plugin manager, <a href="https://neovim.io/doc/user/pack/#vim.pack"><code>vim.pack</code></a>. It was contributed mainly by Evgeni Chasnovski (known for mini.nvim). <a href="https://echasnovski.com/blog/2026-03-13-a-guide-to-vim-pack">His guide of this new component is worth a read</a>. In particular, you can pin plugins to a particular hash, for a version you have audited and update only once you have audited the changes in any new version.</li>
<li>The default status line was reworked and integrates with <code>vim.diagnostic.status()</code>, <code>vim.ui.progress_status()</code> and an indicator for the new <code>busy</code> state. This will allow me to remove some custom logic to produce diagnostic status (<code>E:2 W:3</code> for 2 errors and 3 warnings) and maybe even drop my custom status line code entirely.</li>
<li>I will try to use the <code>MarkSet</code> to make user-placed marks visible in the gutter of the current buffer. I used plugins for that feature in the past, but it should be simple to implement it in configuration with <code>MarkSet</code> now.</li>
<li>I’ll use <code>vim.net.request()</code> to replace some calls to external commands. It will make the configuration more portable and shorter — it might even be marginally faster, eliminating an external command call.</li>
<li><code>'diffopt'</code> <code>inline</code> and <code>inline:word</code> will provide richer diff at the line level, a bit like <a href="https://dandavison.github.io/delta/">delta</a>.</li>
<li>It’s nice to have native plugins for the undo tree (<code>:Undotree</code>) and to compare whole folders (<code>:DiffTool</code>).</li>
<li>I plan to use the new treesitter selection shortcuts (<code>an</code>, <code>in</code>, <code>[n</code> and <code>]n</code> in visual mode) to replace the deprecated <a href="https://github.com/mfussenegger/nvim-treehopper">treehopper</a> plugin: I don’t really care about jumping to a particular highlight node, visual mode should work better for me.</li>
</ul>
<h2 id="polish-of-existing-features">Polish of existing features</h2>
<p>This release introduces some nice <a href="https://neovim.io/doc/user/news-0.12/#_performance">performance improvements</a>, for instance on <code>Ctrl+r</code> in insert mode and <code>packadd</code>.</p>
<p>Some handy shortcuts were added:</p>




  <figure>
    <blockquote cite="https://neovim.io/doc/user/news-0.12/">
      <ul>
<li><code>grt</code> in Normal mode maps to vim.lsp.buf.type_definition()</li>
<li><code>grx</code> in Normal mode maps to vim.lsp.codelens.run()</li>
<li><code>gx</code> in help buffers opens the online documentation for the tag at cursor</li>
</ul>

    </blockquote>
    
      <figcaption class="blockquote-caption">
        
          <cite style="text-align: right"><a href="https://neovim.io/doc/user/news-0.12/">https://neovim.io/doc/user/news-0.12/</a></cite>
          <br/>
        
        
      </figcaption>
    
  </figure>



<p>Finally, the UI was reworked: it is now less coupled with the core. So the core can be <code>:restart</code>ed or <code>:connect</code>ed to with the same UI.
The <code>ui2</code> opt-in should also remove the dreaded “Press ENTER” message.
I plan to give it a try and go back to <code>cmdheight=0</code>.
Both of these settings are marked experimental, but if they are stable enough for me, I might get one more line for files.</p>




  
  
  
  

  <div class="alert alert-edit">
    <p class="alert-heading">
      ✏
      
        Edit
      
    </p>
    <p>2026-03-30: Changed <a href="https://neovim.io/doc/user/news/">https://neovim.io/doc/user/news/</a> to point to <a href="https://neovim.io/doc/user/news-0.12/">https://neovim.io/doc/user/news-0.12/</a>, since the former changed after publishing this post. Further explain why I think <code>complete</code>/<code>autocomplete</code> could help standardize the ecosystem.
2026-04-05: Add a link to <a href="https://neovim.io/doc/user/pack/#vim.pack">vim.pack</a></p>
  </div>



]]></content:encoded></item><item><title>Vim Registers</title><link>https://cj.rs/reg/</link><pubDate>Thu, 29 Jan 2026 14:05:41 +0000</pubDate><guid>https://cj.rs/reg/</guid><description>A quirky but powerful feature.</description><content:encoded><![CDATA[<h2 id="copy-paste-history">Copy-paste History</h2>
<h3 id="implicit">Implicit</h3>
<table>
  <thead>
      <tr>
          <th style="text-align: center">Register</th>
          <th>Content</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: center"><code>&quot;</code></td>
          <td>Effectively last used register. Writes to register <code>0</code>.</td>
      </tr>
      <tr>
          <td style="text-align: center"><code>0</code></td>
          <td>Last yank.</td>
      </tr>
      <tr>
          <td style="text-align: center"><code>1</code></td>
          <td><code>d</code>/<code>c</code> with <code>%</code>,<code>(</code>, <code>)</code>, <code>`</code>, <code>/</code>, <code>?</code>, <code>n</code>, <code>N</code>, <code>{</code> and <code>}</code> or whole line.</td>
      </tr>
      <tr>
          <td style="text-align: center"><code>-</code></td>
          <td>Last small delete (less than a line). Can be the same as <code>1</code>.</td>
      </tr>
      <tr>
          <td style="text-align: center"><code>2</code>-<code>9</code></td>
          <td>Last content of <code>1</code>, <code>2</code>, etc. <code>9</code> is lost.</td>
      </tr>
      <tr>
          <td style="text-align: center"><code>+</code> <code>*</code></td>
          <td>System clipboards (X11/Wayland clipboard and primary).</td>
      </tr>
  </tbody>
</table>
<h3 id="explicit">Explicit</h3>
<table>
  <thead>
      <tr>
          <th style="text-align: center">Register</th>
          <th>Content</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: center"><code>a</code>-<code>z</code></td>
          <td>Named, use freely. Doesn’t fill numbered registers.</td>
      </tr>
      <tr>
          <td style="text-align: center"><code>A</code>-<code>Z</code></td>
          <td>Append to that named register.</td>
      </tr>
  </tbody>
</table>
<h2 id="read-only">Read-Only</h2>
<table>
  <thead>
      <tr>
          <th style="text-align: center">Register</th>
          <th>Content</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: center"><code>.</code></td>
          <td>Last inserted text (similar to <code>.</code> to repeat).</td>
      </tr>
      <tr>
          <td style="text-align: center"><code>%</code></td>
          <td>Current file name.</td>
      </tr>
      <tr>
          <td style="text-align: center"><code>#</code></td>
          <td>Alternate file name.</td>
      </tr>
      <tr>
          <td style="text-align: center"><code>:</code></td>
          <td>Last executed command line. Run with <code>@:</code>.</td>
      </tr>
  </tbody>
</table>
<h2 id="no-read-or-write">No Read or Write</h2>
<table>
  <thead>
      <tr>
          <th style="text-align: center">Register</th>
          <th>Content</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td style="text-align: center"><code>=</code></td>
          <td>Run an expression.</td>
      </tr>
      <tr>
          <td style="text-align: center"><code>_</code></td>
          <td>Black hole.</td>
      </tr>
  </tbody>
</table>
]]></content:encoded></item><item><title>Cargo Info in Neovim, or How Simple Features Go a Long Way</title><link>https://cj.rs/blog/cargo-info-in-neovim/</link><pubDate>Mon, 21 Oct 2024 22:39:34 +0100</pubDate><guid>https://cj.rs/blog/cargo-info-in-neovim/</guid><description>Integrate Vim and Cargo info with just a few lines of Lua using keywordprg.</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-tldr">
    <p class="alert-heading">
      ⚡
      
        TL;DR
      
    </p>
    <p>Open <code>cargo info</code> in Vim or neovim for the package under the cursor using <a href="#introducing-keywordprg">these 4 lines of Lua</a>.</p>
  </div>



<h2 id="cargo-info">Cargo info</h2>
<p><a href="https://blog.rust-lang.org/2024/10/17/Rust-1.82.0.html#whats-in-1820-stable">Rust 1.82</a> was released a couple of days ago.
It’s packed with improvements, but one in particular caught my eye.
Cargo now has a <code>info</code> sub-command.
It displays details about a package in the registry from the comfort of your terminal.
Here is an example:</p>
<pre tabindex="0"><code>$ cargo info lazy_static
lazy_static #macro #lazy #static
A macro for declaring lazily evaluated statics in Rust.
version: 1.5.0
license: MIT OR Apache-2.0
rust-version: unknown
documentation: https://docs.rs/lazy_static
repository: https://github.com/rust-lang-nursery/lazy-static.rs
crates.io: https://crates.io/crates/lazy_static/1.5.0
features:
  spin        = [dep:spin]
  spin_no_std = [spin]
note: to see how you depend on lazy_static, run `cargo tree --invert --package lazy_static@1.5.0`
</code></pre><p>Vim and neovim generally composes well with other terminal tools.
So how can we easily integrate <code>cargo info</code> and <code>neovim</code>?</p>
<h2 id="demo">Demo</h2>
<p>The goal is to press a key and display the <code>cargo info</code> for the crate under the cursor in a <code>Cargo.toml</code> file.
Like this:</p>
<div id="demo3"></div>
<script>
AsciinemaPlayer.create("/blog/cargo-info-in-neovim/demo.json", document.getElementById('demo3'), {
"idleTimeLimit":  1 ,"poster": "npt:3","preload":  1 ,"speed": "1.5",
});
</script>
<noscript><blockquote><p>To run this asciicast without javascript, use <code>asciinema play https://cj.rs/blog/cargo-info-in-neovim/demo.json</code> with <a href="https://asciinema.org/">Asciinema</a></p></blockquote></noscript>

<h2 id="introducing-keywordprg">Introducing <code>keywordprg</code></h2>
<p>In the above demonstration, we press <a href="https://neovim.io/doc/user/various.html#K"><code>K</code></a> over a crates name. Then neovim executes the program set in <a href="https://neovim.io/doc/user/options.html#'keywordprg'"><code>keywordprg</code></a>, appending the work under the cursor. So if the cursor is on <code>lazy_static</code></p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-toml" data-lang="toml"><span style="display:flex;"><span>[<span style="color:#e06c75">dev-dependencies</span>]
</span></span><span style="display:flex;"><span><span style="color:#e06c75">iai</span> = <span style="color:#98c379">&#34;0.1&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#e06c75">insta</span> = <span style="color:#98c379">&#34;1.40.0&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#e06c75">lazy_static</span> = <span style="color:#98c379">&#34;1.5.0&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#7f848e">#   ^ cursor is here when we press K</span>
</span></span><span style="display:flex;"><span><span style="color:#e06c75">mktemp</span> = <span style="color:#98c379">&#34;0.5&#34;</span>
</span></span></code></pre></div><p>and <code>keywordprg</code> is set to <code>cargo info</code>, the command <code>cargo info lazy_static</code> is executed in a new terminal.</p>
<p>We can further improve this.
Since we want a fast answer (without checking the remote repository), let’s use <code>cargo info --offline</code>.
And we would like pretty colors, let’s force that using <code>--color=always</code>.</p>
<p>We only want to alter <code>keywordprg</code> when a <code>Cargo.toml</code> file is edited, because it makes no sense to call <code>cargo info</code> in a Python file.
We can use a <a href="https://neovim.io/doc/user/usr_41.html#ftplugin"><code>ftplugin</code></a> for toml files for that<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, so that the plugin is only loaded and executed when a toml file is executed.
Furthermore we can change the setting only when opening a file named “Cargo.toml”, instead of changing it for every toml file.
We can put the necessary configuration in <code>~/.config/nvim/ftplugin/toml.lua</code><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>, like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#c678dd">if</span> <span style="color:#e06c75">vim.endswith</span>(<span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">bufname</span>(), <span style="color:#98c379">&#34;Cargo.toml&#34;</span>) <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">vim.opt_local</span>.<span style="color:#e06c75">keywordprg</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;cargo info --color=always --offline&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#7f848e">-- Add chars that are often part of keys, especially in rust crates</span>
</span></span><span style="display:flex;"><span><span style="color:#7f848e">-- (https://toml.io/en/v1.0.0#keys)</span>
</span></span><span style="display:flex;"><span><span style="color:#e06c75">vim.opt_local</span>.<span style="color:#e06c75">iskeyword</span>:<span style="color:#e06c75">append</span> <span style="color:#98c379">&#34;-&#34;</span>
</span></span></code></pre></div><p>The Lua code above also changes the <a href="https://neovim.io/doc/user/options.html#'iskeyword'"><code>iskeyword</code></a> preference.
This is so that <code>tokio-test</code> is considered as one word (a “keyword” in Vim parlance), instead of two (<code>tokio</code> and <code>test</code>).
With this set, doing <code>K</code> on <code>tokio-test</code> will behave properly, because <code>tokio-test</code> is passed to the command, instead of just <code>tokio</code>.</p>
<p>One final note: the above configuration was the only thing sourced in the demo.
All the rest is default, vanilla, neovim 0.10.2.
And this does not even require recent neovim features, it has been supported in Vim for many years.</p>
<h2 id="learnings">Learnings</h2>
<p>Obviously this could be further refined.
Other options could be passed to <code>cargo info</code>, like <code>-v</code> to make it more verbose and list dependencies.
Some more scripting could add useful features to further improve the integration.
For this particular problem, there are even <a href="https://github.com/saecki/crates.nvim">a full plugin</a> with many more features.
But the point is to showcase a simple and robust integration<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, applicable in a wide variety of contexts.
In particular when the use case is too niche, a full plugin is too costly to write and maintain for the benefits it provides.</p>
<p>Let’s highlight the takeaways from this post that are applicable in a wide variety of contexts.</p>
<p>First, a number of Neovim commands can do something with the keyword under the cursor.
Adjusting what counts as a keyword boundary is often, but not always, done in the syntax file of a particular language.
Or you may just want to make a different trade off and set your own <code>iskeyword</code> value.</p>
<p>Second, the <a href="https://neovim.io/doc/user/various.html#K"><code>K</code></a> mapping is often used to look up a word in a documentation (it is set to open man pages by default).
It even works in visual mode, looking up the selected text.
And arbitrary Vim or shell commands can be used, instead of the default <code>:Man</code>.</p>
<p>Please do read the corresponding parts of the Vim manual linked in this section to learn more.
Next time, you may come up with your own quick integration between Vim and another tool.
Happy hacking!</p>




  
  
  
  

  <div class="alert alert-edit">
    <p class="alert-heading">
      ✏
      
        Edit
      
    </p>
    <p>2024-10-22: Clarify why we use nvim settings instead of a plugin like <a href="https://github.com/saecki/crates.nvim">crates.nvim</a></p>
  </div>



<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>To keep the example simple, we don’t clean-up the local preferences that are set here. See the <a href="https://neovim.io/doc/user/usr_41.html#ftplugin">documentation</a> for best practices when sharing ftplugins.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>It’s also possible to do this in Vimscript instead of Lua.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Thanks to <a href="https://fosstodon.org/@dpom">@dpom@fosstodon.org</a> <a href="https://fosstodon.org/@dpom/113347670141692005">for pointing out</a> that crates.nvim has that feature already.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>onedark-fast.nvim</title><link>https://cj.rs/onedark-fast-nvim/</link><pubDate>Wed, 12 Jun 2024 22:02:37 +0100</pubDate><guid>https://cj.rs/onedark-fast-nvim/</guid><description>Fast OneDark colorscheme for neovim</description><content:encoded><![CDATA[
<p style="display: flex; justify-content: space-between">
  <a href="https://github.com/cljoly/onedark-fast.nvim" data-goatcounter-click="ext-github-onedark-fast.nvim" data-goatcounter-title="cljoly/onedark-fast.nvim">
    <span class="svgicon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
    stroke-linecap="round" stroke-linejoin="round">
    <path
        d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22">
    </path>
</svg></span>&nbsp;cljoly/onedark-fast.nvim
  </a>
  <a class="badges" href="https://github.com/cljoly/onedark-fast-nvim" data-goatcounter-click="ext-stargithub-onedark-fast.nvim" data-goatcounter-title="stars cljoly/onedark-fast.nvim">
    <img src="https://img.shields.io/github/stars/cljoly/onedark-fast.nvim?style=social" alt="Github stars for onedark-fast.nvim">
  </a>
</p>


<div class="badges">

<p>

  <img alt="Neovim version" loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDUiIGhlaWdodD0iMjAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iTmVvdmltOiAwLjcrIj48dGl0bGU+TmVvdmltOiAwLjcrPC90aXRsZT48bGluZWFyR3JhZGllbnQgaWQ9InMiIHgyPSIwIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjYmJiIiBzdG9wLW9wYWNpdHk9Ii4xIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xIi8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9InIiPjxyZWN0IHdpZHRoPSIxMDUiIGhlaWdodD0iMjAiIHJ4PSIzIiBmaWxsPSIjZmZmIi8+PC9jbGlwUGF0aD48ZyBjbGlwLXBhdGg9InVybCgjcikiPjxyZWN0IHdpZHRoPSI2OCIgaGVpZ2h0PSIyMCIgZmlsbD0iIzU1NSIvPjxyZWN0IHg9IjY4IiB3aWR0aD0iMzciIGhlaWdodD0iMjAiIGZpbGw9IiM1N2ExNDMiLz48cmVjdCB3aWR0aD0iMTA1IiBoZWlnaHQ9IjIwIiBmaWxsPSJ1cmwoI3MpIi8+PC9nPjxnIGZpbGw9IiNmZmYiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJWZXJkYW5hLEdlbmV2YSxEZWphVnUgU2FucyxzYW5zLXNlcmlmIiB0ZXh0LXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiBmb250LXNpemU9IjExMCI+PGltYWdlIHg9IjUiIHk9IjMiIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgaHJlZj0iZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCbWFXeHNQU0lqTlRkQk1UUXpJaUJ5YjJ4bFBTSnBiV2NpSUhacFpYZENiM2c5SWpBZ01DQXlOQ0F5TkNJZ2VHMXNibk05SW1oMGRIQTZMeTkzZDNjdWR6TXViM0puTHpJd01EQXZjM1puSWo0OGRHbDBiR1UrVG1WdmRtbHRQQzkwYVhSc1pUNDhjR0YwYUNCa1BTSk5NaTR5TVRRZ05DNDVOVFIyTVRNdU5qRTFURGN1TmpVMUlESTBWakV3TGpNeE5Fd3pMak14TWlBekxqZzBOU0F5TGpJeE5DQTBMamsxTkhwdE5DNDVPVGtnTVRjdU9UaHNMVFF1TlRVM0xUUXVOVFE0VmpVdU1UTTJiQzQxT1MwdU5UazJJRE11T1RZM0lEVXVPVEE0ZGpFeUxqUTROWHB0TVRRdU5UY3pMVFF1TkRVM2JDMHVPRFl5TGprek55MDBMakkwTFRZdU16YzJWakJzTlM0d05qZ2dOUzR3T1RJdU1ETTBJREV6TGpNNE5YcE5OeTQwTXpFdU1EQXhiREV5TGprNU9DQXhPUzQ0TXpVdE15NDJNemNnTXk0Mk16ZE1NeTQzT0RjZ015NDJPRE1nTnk0ME15QXdlaUl2UGp3dmMzWm5QZz09Ii8+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9IjQzNSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iNDEwIj5OZW92aW08L3RleHQ+PHRleHQgeD0iNDM1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSI0MTAiPk5lb3ZpbTwvdGV4dD48dGV4dCBhcmlhLWhpZGRlbj0idHJ1ZSIgeD0iODU1IiB5PSIxNTAiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiB0ZXh0TGVuZ3RoPSIyNzAiPjAuNys8L3RleHQ+PHRleHQgeD0iODU1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSIyNzAiPjAuNys8L3RleHQ+PC9nPjwvc3ZnPg=="> <a href="https://cj.rs/riss">

  <img loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0icG93ZXJlZCBieTogcmlzcyI+PHRpdGxlPnBvd2VyZWQgYnk6IHJpc3M8L3RpdGxlPjxsaW5lYXJHcmFkaWVudCBpZD0icyIgeDI9IjAiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNiYmIiIHN0b3Atb3BhY2l0eT0iLjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iLjEiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iciI+PHJlY3Qgd2lkdGg9IjEwNCIgaGVpZ2h0PSIyMCIgcng9IjMiIGZpbGw9IiNmZmYiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNyKSI+PHJlY3Qgd2lkdGg9Ijc1IiBoZWlnaHQ9IjIwIiBmaWxsPSIjNTU1Ii8+PHJlY3QgeD0iNzUiIHdpZHRoPSIyOSIgaGVpZ2h0PSIyMCIgZmlsbD0iIzlmOWY5ZiIvPjxyZWN0IHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIGZpbGw9InVybCgjcykiLz48L2c+PGcgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IlZlcmRhbmEsR2VuZXZhLERlamFWdSBTYW5zLHNhbnMtc2VyaWYiIHRleHQtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iIGZvbnQtc2l6ZT0iMTEwIj48dGV4dCBhcmlhLWhpZGRlbj0idHJ1ZSIgeD0iMzg1IiB5PSIxNTAiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgeD0iMzg1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9Ijg4NSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0Pjx0ZXh0IHg9Ijg4NSIgeT0iMTQwIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgZmlsbD0iI2ZmZiIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0PjwvZz48L3N2Zz4="></a></p>

</div>

<p>Port of <a href="https://github.com/navarasu/onedark.nvim">onedark</a> to <a href="https://fennel-lang.org/">Fennel</a>, so that we generate fast Lua code.</p>
<h2 id="work-in-progress">Work in Progress</h2>
<p>I have been daily driving this for a few years, but there may still be bugs. A number of plugins are not supported (mostly because I don’t use them). At some point, I would like to provide easier personnalisation, but it’s not quite there yet. If you want to customize this colorscheme, you will have to change the <a href="https://fennel-lang.org/">Fennel</a> code and recompile it.</p>
<p>A few highlight group might still refer to NVim default colorscheme. They can be found by running <code>:highlight</code> and then searching the output for <code>Nvim</code>.</p>
]]></content:encoded></item><item><title>Neovim Plugins</title><link>https://cj.rs/open-source/nvim-plugins/</link><pubDate>Thu, 29 Feb 2024 21:00:23 +0200</pubDate><guid>https://cj.rs/open-source/nvim-plugins/</guid><description>&lt;p&gt;List of my plugins for the neovim text editor.&lt;/p&gt;</description><content:encoded><![CDATA[<p>List of my plugins for the neovim text editor.</p>
<div class="projects">





<div class="projects-inner">

<article class="project-entry" onclick="location.href='https:\/\/cj.rs\/telescope-repo.nvim\/';" style="cursor:pointer;">
  <header class="entry-header">
    <h3>
        <a class="" 
            aria-label="post link to telescope-repo.nvim"
            data-goatcounter-click="ext-telescope-repo.nvim"
            data-goatcounter-referrer="nvim-plugins"
            href="https://cj.rs/telescope-repo.nvim/">Telescope Repo Nvim</a>
    </h3>
  </header>
  <section class="entry-content">
    <p>🦘 Jump into the repositories (git, mercurial…) of your filesystem with telescope.nvim, without any setup</p>
  </section>
  <footer class="entry-footer project-footer">
      <p>
        <span class="project-circle">
        <svg  width="11" height="12">
          <circle cx="4" cy="7.5" r="4" fill="#000080" />
        </svg>
        </span>
        Lua
        <span>&nbsp;★&nbsp;241</span>
      </p>
  </footer>
</article>


<article class="project-entry" onclick="location.href='https:\/\/cj.rs\/bepo-nvim\/';" style="cursor:pointer;">
  <header class="entry-header">
    <h3>
        <a class="" 
            aria-label="post link to bepo.nvim"
            data-goatcounter-click="ext-bepo.nvim"
            data-goatcounter-referrer="nvim-plugins"
            href="https://cj.rs/bepo-nvim/">Bepo Nvim</a>
    </h3>
  </header>
  <section class="entry-content">
    <p>⌨️ Mappings for the bepo keyboard layout in Lua, inspired by <a href="https://github.com/michamos/vim-bepo">https://github.com/michamos/vim-bepo</a></p>
  </section>
  <footer class="entry-footer project-footer">
      <p>
        <span class="project-circle">
        <svg  width="11" height="12">
          <circle cx="4" cy="7.5" r="4" fill="#fff3d7" />
        </svg>
        </span>
        Fennel
        <span>&nbsp;★&nbsp;18</span>
      </p>
  </footer>
</article>


<article class="project-entry" onclick="location.href='https:\/\/cj.rs\/minimal-format-nvim';" style="cursor:pointer;">
  <header class="entry-header">
    <h3>
        <a class="" 
            aria-label="post link to minimal-format.nvim"
            data-goatcounter-click="ext-minimal-format.nvim"
            data-goatcounter-referrer="nvim-plugins"
            href="https://cj.rs/minimal-format-nvim">Minimal Format Nvim</a>
    </h3>
  </header>
  <section class="entry-content">
    <p>Minimal, smart formatting using native neovim capabilities.</p>
  </section>
  <footer class="entry-footer project-footer">
      <p>
        <span class="project-circle">
        <svg  width="11" height="12">
          <circle cx="4" cy="7.5" r="4" fill="#000080" />
        </svg>
        </span>
        Lua
        
      </p>
  </footer>
</article>


</div>


</div>

<h2 id="work-in-progress">Work in progress</h2>
<div class="projects">





<div class="projects-inner">

<article class="project-entry" onclick="location.href='https:\/\/cj.rs\/onedark-fast-nvim\/';" style="cursor:pointer;">
  <header class="entry-header">
    <h3>
        <a class="" 
            aria-label="post link to onedark-fast.nvim"
            data-goatcounter-click="ext-onedark-fast.nvim"
            data-goatcounter-referrer="nvim-plugins"
            href="https://cj.rs/onedark-fast-nvim/">Onedark Fast Nvim</a>
    </h3>
  </header>
  <section class="entry-content">
    <p>Port of onedark to fennel, so that we generate fast Lua code.</p>
  </section>
  <footer class="entry-footer project-footer">
      <p>
        <span class="project-circle">
        <svg  width="11" height="12">
          <circle cx="4" cy="7.5" r="4" fill="#000080" />
        </svg>
        </span>
        Lua
        
      </p>
  </footer>
</article>


</div>


</div>]]></content:encoded></item><item><title>minimal-format.nvim</title><link>https://cj.rs/minimal-format-nvim/</link><pubDate>Wed, 13 Sep 2023 10:02:37 +0100</pubDate><guid>https://cj.rs/minimal-format-nvim/</guid><description>Smart formatting for neovim</description><content:encoded><![CDATA[
<p style="display: flex; justify-content: space-between">
  <a href="https://github.com/cljoly/minimal-format.nvim" data-goatcounter-click="ext-github-minimal-format.nvim" data-goatcounter-title="cljoly/minimal-format.nvim">
    <span class="svgicon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
    stroke-linecap="round" stroke-linejoin="round">
    <path
        d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22">
    </path>
</svg></span>&nbsp;cljoly/minimal-format.nvim
  </a>
  <a class="badges" href="https://github.com/cljoly/minimal-format-nvim" data-goatcounter-click="ext-stargithub-minimal-format.nvim" data-goatcounter-title="stars cljoly/minimal-format.nvim">
    <img src="https://img.shields.io/github/stars/cljoly/minimal-format.nvim?style=social" alt="Github stars for minimal-format.nvim">
  </a>
</p>


<div class="badges">

<p>

  <img alt="Neovim version" loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NSIgaGVpZ2h0PSIyMCIgcm9sZT0iaW1nIiBhcmlhLWxhYmVsPSJOZW92aW06IDAuOSI+PHRpdGxlPk5lb3ZpbTogMC45PC90aXRsZT48bGluZWFyR3JhZGllbnQgaWQ9InMiIHgyPSIwIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjYmJiIiBzdG9wLW9wYWNpdHk9Ii4xIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xIi8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9InIiPjxyZWN0IHdpZHRoPSI5NSIgaGVpZ2h0PSIyMCIgcng9IjMiIGZpbGw9IiNmZmYiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNyKSI+PHJlY3Qgd2lkdGg9IjY4IiBoZWlnaHQ9IjIwIiBmaWxsPSIjNTU1Ii8+PHJlY3QgeD0iNjgiIHdpZHRoPSIyNyIgaGVpZ2h0PSIyMCIgZmlsbD0iIzU3YTE0MyIvPjxyZWN0IHdpZHRoPSI5NSIgaGVpZ2h0PSIyMCIgZmlsbD0idXJsKCNzKSIvPjwvZz48ZyBmaWxsPSIjZmZmIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iVmVyZGFuYSxHZW5ldmEsRGVqYVZ1IFNhbnMsc2Fucy1zZXJpZiIgdGV4dC1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiIgZm9udC1zaXplPSIxMTAiPjxpbWFnZSB4PSI1IiB5PSIzIiB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIGhyZWY9ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5Qm1hV3hzUFNJak5UZEJNVFF6SWlCeWIyeGxQU0pwYldjaUlIWnBaWGRDYjNnOUlqQWdNQ0F5TkNBeU5DSWdlRzFzYm5NOUltaDBkSEE2THk5M2QzY3Vkek11YjNKbkx6SXdNREF2YzNabklqNDhkR2wwYkdVK1RtVnZkbWx0UEM5MGFYUnNaVDQ4Y0dGMGFDQmtQU0pOTWk0eU1UUWdOQzQ1TlRSMk1UTXVOakUxVERjdU5qVTFJREkwVmpFd0xqTXhORXd6TGpNeE1pQXpMamcwTlNBeUxqSXhOQ0EwTGprMU5IcHROQzQ1T1RrZ01UY3VPVGhzTFRRdU5UVTNMVFF1TlRRNFZqVXVNVE0yYkM0MU9TMHVOVGsySURNdU9UWTNJRFV1T1RBNGRqRXlMalE0TlhwdE1UUXVOVGN6TFRRdU5EVTNiQzB1T0RZeUxqa3pOeTAwTGpJMExUWXVNemMyVmpCc05TNHdOamdnTlM0d09USXVNRE0wSURFekxqTTROWHBOTnk0ME16RXVNREF4YkRFeUxqazVPQ0F4T1M0NE16VXRNeTQyTXpjZ015NDJNemRNTXk0M09EY2dNeTQyT0RNZ055NDBNeUF3ZWlJdlBqd3ZjM1puUGc9PSIvPjx0ZXh0IGFyaWEtaGlkZGVuPSJ0cnVlIiB4PSI0MzUiIHk9IjE1MCIgZmlsbD0iIzAxMDEwMSIgZmlsbC1vcGFjaXR5PSIuMyIgdHJhbnNmb3JtPSJzY2FsZSguMSkiIHRleHRMZW5ndGg9IjQxMCI+TmVvdmltPC90ZXh0Pjx0ZXh0IHg9IjQzNSIgeT0iMTQwIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgZmlsbD0iI2ZmZiIgdGV4dExlbmd0aD0iNDEwIj5OZW92aW08L3RleHQ+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9IjgwNSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iMTcwIj4wLjk8L3RleHQ+PHRleHQgeD0iODA1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSIxNzAiPjAuOTwvdGV4dD48L2c+PC9zdmc+"> <a href="https://cj.rs/riss">

  <img loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0icG93ZXJlZCBieTogcmlzcyI+PHRpdGxlPnBvd2VyZWQgYnk6IHJpc3M8L3RpdGxlPjxsaW5lYXJHcmFkaWVudCBpZD0icyIgeDI9IjAiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNiYmIiIHN0b3Atb3BhY2l0eT0iLjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iLjEiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iciI+PHJlY3Qgd2lkdGg9IjEwNCIgaGVpZ2h0PSIyMCIgcng9IjMiIGZpbGw9IiNmZmYiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNyKSI+PHJlY3Qgd2lkdGg9Ijc1IiBoZWlnaHQ9IjIwIiBmaWxsPSIjNTU1Ii8+PHJlY3QgeD0iNzUiIHdpZHRoPSIyOSIgaGVpZ2h0PSIyMCIgZmlsbD0iIzlmOWY5ZiIvPjxyZWN0IHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIGZpbGw9InVybCgjcykiLz48L2c+PGcgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IlZlcmRhbmEsR2VuZXZhLERlamFWdSBTYW5zLHNhbnMtc2VyaWYiIHRleHQtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iIGZvbnQtc2l6ZT0iMTEwIj48dGV4dCBhcmlhLWhpZGRlbj0idHJ1ZSIgeD0iMzg1IiB5PSIxNTAiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgeD0iMzg1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9Ijg4NSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0Pjx0ZXh0IHg9Ijg4NSIgeT0iMTQwIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgZmlsbD0iI2ZmZiIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0PjwvZz48L3N2Zz4="></a> 
<img alt="GitHub tag (latest SemVer)" loading="lazy" src="https://img.shields.io/github/v/tag/cljoly/minimal-format.nvim?color=darkgreen&sort=semver"> 
<img alt="Endpoint Badge" loading="lazy" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fd.cj.rs%2Fnvim%2Fminimal-format.json&cacheSeconds=90000"></p>

</div>

<p>A simple &amp; powerful formatting plugin that extends neovim just a little bit to automatically format your code.</p>
<ul>
<li>Native: uses the <code>formatprg</code> setting, the setting used by the <code>gq</code> mapping in default vim.</li>
<li>Safe:
<ul>
<li>Doesn’t replace your code with garbage if the formatting command fails (unlike <code>gq</code>)</li>
<li>Applies the minimum difference required to the buffer. This means that the whole buffer won’t be changed and marks and external marks (used e.g. for diagnostics) will be preserved. This makes formatting less disruptive to your workflow</li>
</ul>
</li>
<li>Asynchronous: neovim won’t hang while the formatting command is running</li>
<li>Can be configured to trigger automatically on write, per buffer</li>
<li>No startup cost: call the functions you need when you need them, no need to load anything during startup.</li>
</ul>
<h2 id="getting-started">Getting started</h2>
<p>Install this package with your favorite package manager.</p>
<p>Then, you can configure some mappings or user commands to the following functions:</p>
<ul>
<li><code>require(&quot;minimal-format&quot;).format_with_formatprg(bufnr)</code>: format the buffer number <code>bufnr</code></li>
<li><code>require(&quot;minimal-format&quot;).toggle_autocmd</code>: toggle automatic formatting on write</li>
</ul>
<h3 id="setting-up-a-mapping-to-format-the-current-file">Setting Up a Mapping to Format the Current File</h3>
<p>Add the following to your <code>init.lua</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">vim.keymap</span>.<span style="color:#e06c75">set</span>(<span style="color:#98c379">&#34;n&#34;</span>, <span style="color:#98c379">&#34;&lt;space&gt;f&#34;</span>, <span style="color:#c678dd">function</span>()
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;minimal-format&#34;</span>).<span style="color:#e06c75">format_with_formatprg</span>(<span style="color:#d19a66">0</span>, <span style="color:#e5c07b">false</span>)
</span></span><span style="display:flex;"><span><span style="color:#c678dd">end</span>, { <span style="color:#e06c75">desc</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;Format current buffer, using formatprg when possible&#34;</span> })
</span></span></code></pre></div><h3 id="setting-a-formatter-per-file-type">Setting a Formatter per File Type</h3>
<p>Configure <code>formatprg</code> like you normally would to use <code>gq</code>. For instance, it might look like this for C:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-vim" data-lang="vim"><span style="display:flex;"><span><span style="color:#c678dd">if</span> <span style="color:#e06c75">executable</span>(<span style="color:#98c379">&#39;clang-format&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">setlocal</span> <span style="color:#e06c75">formatprg</span>=<span style="color:#e06c75">clang</span>-<span style="color:#e06c75">format</span>\ --<span style="color:#e06c75">assume</span>-<span style="color:#e06c75">filename</span>=<span style="color:#e06c75">a</span>.<span style="color:#e06c75">c</span>
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">let</span> <span style="color:#e06c75">b</span>:<span style="color:#e06c75">undo_ftplugin</span> .= <span style="color:#98c379">&#39; fp&lt;&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#c678dd">endif</span>
</span></span></code></pre></div><p>Paste the above snippet in <code>.config/nvim/ftplugin/c.vim</code>.
Then when you call <code>require(&quot;minimal-format&quot;).format_with_formatprg(0)</code>, the current buffer will be asynchronously formatted with this command.</p>
<h3 id="per-buffer-settings">Per Buffer Settings</h3>
<p>Example for Rust, in <code>.config/nvim/ftplugin/rust.lua</code>, where we automatically detect the rust edition:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#c678dd">if</span> <span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">executable</span> <span style="color:#98c379">&#34;rustfmt&#34;</span> <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">vim.opt_local</span>.<span style="color:#e06c75">formatprg</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;rustfmt -q --emit=stdout&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">local</span> <span style="color:#e06c75">edition</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;cj.rust&#34;</span>).<span style="color:#e06c75">find_rust_edition</span>()
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">if</span> <span style="color:#e06c75">edition</span> <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">vim.opt_local</span>.<span style="color:#e06c75">formatprg</span>:<span style="color:#e06c75">append</span>(<span style="color:#98c379">&#34; --edition &#34;</span> <span style="color:#56b6c2">..</span> <span style="color:#e06c75">edition</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">vim.b</span>.<span style="color:#e06c75">undo_ftplugin</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">vim.b</span>.<span style="color:#e06c75">undo_ftplugin</span> <span style="color:#56b6c2">..</span> <span style="color:#98c379">&#34;|setl fp&lt;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;minimal-format&#34;</span>).<span style="color:#e06c75">enable_autocmd</span>(<span style="color:#d19a66">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#c678dd">end</span>
</span></span></code></pre></div><p>where <code>find_rust_edition</code> is defined like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#c678dd">function</span> <span style="color:#e5c07b">M</span>.<span style="color:#61afef;font-weight:bold">find_rust_edition</span>()
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">local</span> <span style="color:#e06c75">manifests</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">find_cargotoml</span>()
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">for</span> <span style="color:#e06c75">_</span>, <span style="color:#e06c75">manifest</span> <span style="color:#c678dd">in</span> <span style="color:#e06c75">ipairs</span>(<span style="color:#e06c75">manifests</span>) <span style="color:#c678dd">do</span>
</span></span><span style="display:flex;"><span>    <span style="color:#c678dd">local</span> <span style="color:#e06c75">grep_output</span> <span style="color:#56b6c2">=</span>
</span></span><span style="display:flex;"><span>      <span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">system</span> { <span style="color:#98c379">&#34;grep&#34;</span>, <span style="color:#98c379">&#34;-E&#34;</span>, <span style="color:#98c379">&#34;--only-matching&#34;</span>, <span style="color:#98c379">[[edition *= *&#34;(\d+)&#34;]]</span>, <span style="color:#e06c75">manifest</span> }
</span></span><span style="display:flex;"><span>    <span style="color:#c678dd">local</span> <span style="color:#e06c75">year</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">substitute</span>(<span style="color:#e06c75">grep_output</span> <span style="color:#56b6c2">or</span> <span style="color:#98c379">&#34;&#34;</span>, <span style="color:#98c379">[[^.*&#34;\(\d\+\)&#34;.*$]]</span>, <span style="color:#98c379">[[\1]]</span>, <span style="color:#98c379">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c678dd">if</span> <span style="color:#e06c75">year</span> <span style="color:#56b6c2">~=</span> <span style="color:#98c379">&#34;&#34;</span> <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span>      <span style="color:#c678dd">return</span> <span style="color:#e06c75">tonumber</span>(<span style="color:#e06c75">year</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">return</span> <span style="color:#e5c07b">nil</span>
</span></span><span style="display:flex;"><span><span style="color:#c678dd">end</span>
</span></span></code></pre></div><h3 id="automatic-formatting-on-save">Automatic formatting on save</h3>
<p>The last option is to enable automatic formatting before writing a buffer.</p>
<h4 id="lua">Lua</h4>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#c678dd">if</span> <span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">executable</span> <span style="color:#98c379">&#34;stylua&#34;</span> <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">vim.opt_local</span>.<span style="color:#e06c75">formatprg</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;stylua -&#34;</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">vim.b</span>.<span style="color:#e06c75">undo_ftplugin</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">vim.b</span>.<span style="color:#e06c75">undo_ftplugin</span> <span style="color:#56b6c2">..</span> <span style="color:#98c379">&#34; fp&lt;&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;minimal-format&#34;</span>).<span style="color:#e06c75">enable_autocmd</span>(<span style="color:#d19a66">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#c678dd">end</span>
</span></span></code></pre></div><h4 id="vimscript">Vimscript</h4>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-vim" data-lang="vim"><span style="display:flex;"><span><span style="color:#c678dd">if</span> <span style="color:#e06c75">executable</span>(<span style="color:#98c379">&#39;stylua&#39;</span>)
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">setlocal</span> <span style="color:#e06c75">formatprg</span>=<span style="color:#e06c75">stylua</span>\ -
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">let</span> <span style="color:#e06c75">b</span>:<span style="color:#e06c75">undo_ftplugin</span> .= <span style="color:#98c379">&#39; fp&lt;&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">call</span> <span style="color:#e06c75">v</span>:<span style="color:#e06c75">lua</span>.<span style="color:#e06c75">require</span><span style="color:#98c379">&#39;minimal-format&#39;</span>.<span style="color:#e06c75">enable_autocmd</span>(<span style="color:#d19a66">0</span>)
</span></span><span style="display:flex;"><span><span style="color:#c678dd">endif</span>
</span></span></code></pre></div><h2 id="example-formatters-per-language">Example Formatters per Language</h2>
<table>
  <thead>
      <tr>
          <th>Language</th>
          <th><code>formatprg</code></th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>C</td>
          <td>clang-format &ndash;assume-filename=a.c</td>
      </tr>
      <tr>
          <td>Gawk</td>
          <td>gawk &ndash;pretty-print=- -f -</td>
      </tr>
      <tr>
          <td>Go</td>
          <td>goimports</td>
      </tr>
      <tr>
          <td>Lua</td>
          <td>stylua -</td>
      </tr>
      <tr>
          <td>Python</td>
          <td>black -t py36 -q -</td>
      </tr>
      <tr>
          <td>Rust</td>
          <td>rustfmt &ndash;quiet &ndash;emit=stdout</td>
      </tr>
      <tr>
          <td>Toml</td>
          <td>taplo fmt -</td>
      </tr>
  </tbody>
</table>
]]></content:encoded></item><item><title>Luasnip and Treesitter for Smarter Snippets</title><link>https://cj.rs/blog/luasnip-and-treesitter-for-smarter-snippets/</link><pubDate>Wed, 30 Aug 2023 13:28:14 +0100</pubDate><guid>https://cj.rs/blog/luasnip-and-treesitter-for-smarter-snippets/</guid><description>An example on Go error handling.</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-tldr">
    <p class="alert-heading">
      ⚡
      
        TL;DR
      
    </p>
    <p>A context-aware snippet for Go error handling code, returning the right types, with the default values.</p>
<ul>
<li><a href="#demo">Demo</a></li>
<li><a href="#code">Snippet</a></li>
</ul>
  </div>







  
  
  
  

  <div class="alert alert-edit">
    <p class="alert-heading">
      ✏
      
        Edit
      
    </p>
    <p>TJ DeVries made a <a href="https://www.youtube.com/watch?v=aNWx-ym7jjI">video</a> explaining an improved version of this snippet. You may still find the discussion on Go errors in this post interesting.</p>
  </div>



<h2 id="introduction">Introduction</h2>
<p><a href="https://go.dev">Golang’s</a> error handling is notoriously verbose. It was also the top pain point in the <a href="https://go.dev/blog/survey2022-q2-results">Go Developer Survey Q2 2022</a>. <a href="https://github.com/golang/go/issues/56165">Numerous</a> <a href="https://github.com/golang/go/issues/32437">proposals</a> <a href="https://github.com/golang/go/issues/37141">to simplify</a> error handling have been written, but at the time of writing, none have been accepted.</p>
<p>So we have to live with the current state of things. It’s ok, Neovim with Treesitter has a good “understanding” of code, so we can use it to generate the error handling code.</p>
<h3 id="treesitter">Treesitter</h3>
<p>One of <a href="https://github.com/neovim/neovim/releases/tag/v0.5.0">Neovim 0.5</a> most exciting features was the introduction of Treesitter.
This new parsing system gives the editor a basic understanding of the code at hand: the editor “knows” what the function name is, where a variable is defined…
It enables great plugins, <a href="https://github.com/SmiteshP/nvim-gps">showing the context</a> around some code, <a href="https://github.com/nvim-treesitter/nvim-treesitter-textobjects">improved selection</a>, <a href="https://github.com/nvim-treesitter/nvim-treesitter-refactor">better renaming and navigation</a> and more!
In this post, we will look into how Treesitter can be combined with <a href="https://github.com/L3MON4D3/LuaSnip">LuaSnip</a> for smarter snippets.</p>
<h2 id="error-handling-in-go">Error Handling in Go</h2>




  
  
  
  

  <div class="alert alert-note">
    <p class="alert-heading">
      ℹ️
      
        Note
      
    </p>
    <p>Feel free to <a href="#clever">skip</a> this section if you already know about Go error handling.</p>
  </div>



<p>In <a href="https://go.dev">Golang</a>, errors are handled by returning a value of type <code>error</code>, sometimes along the types returned when everything goes well. For instance, the <a href="https://pkg.go.dev/time#Parse"><code>time.Parse</code></a> function has the following signature:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#c678dd">func</span> <span style="color:#61afef;font-weight:bold">Parse</span>(<span style="color:#e06c75">layout</span>, <span style="color:#e06c75">value</span> <span style="color:#e5c07b">string</span>) (<span style="color:#e06c75">Time</span>, <span style="color:#e5c07b">error</span>)
</span></span></code></pre></div><p><code>time.Parse</code> will try to parse a string with the given format.
If it’s successful, it’ll return a value of type <code>Time</code> and the error will be <code>nil</code>.
If a date can’t be parsed, then the error won’t be <code>nil</code>.
And so it’s common in Go to check for errors like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#e06c75">timeStr</span> <span style="color:#56b6c2">:=</span> <span style="color:#98c379">&#34;Aug 6, 2022 @ 7:54pm&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#e06c75">t</span>, <span style="color:#e06c75">err</span> <span style="color:#56b6c2">:=</span> <span style="color:#e06c75">time</span>.<span style="color:#61afef;font-weight:bold">Parse</span>(<span style="color:#98c379">&#34;Jan 2, 2006 @ 3:04pm&#34;</span>, <span style="color:#e06c75">timeStr</span>)
</span></span><span style="display:flex;"><span><span style="color:#c678dd">if</span> <span style="color:#e06c75">err</span> <span style="color:#56b6c2">!=</span> <span style="color:#e5c07b">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">log</span>.<span style="color:#61afef;font-weight:bold">Fatal</span>(<span style="color:#e06c75">err</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#7f848e">// Now, we can safely use t</span>
</span></span></code></pre></div><p>In the example above, we just log the error and panic. But if we are in a function, we will likely return an error to the caller, wrapping the error returned by <code>time.Parse</code>. For instance:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-go" data-lang="go"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">1</span><span><span style="color:#c678dd">func</span> <span style="color:#61afef;font-weight:bold">parseLong</span>(<span style="color:#e06c75">timeStr</span> <span style="color:#e5c07b">string</span>) (<span style="color:#e06c75">Time</span>, <span style="color:#e5c07b">error</span>) {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">2</span><span>    <span style="color:#e06c75">t</span>, <span style="color:#e06c75">err</span> <span style="color:#56b6c2">:=</span> <span style="color:#e06c75">time</span>.<span style="color:#61afef;font-weight:bold">Parse</span>(<span style="color:#98c379">&#34;Jan 2, 2006 @ 3:04pm&#34;</span>, <span style="color:#e06c75">timeStr</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">3</span><span>    <span style="color:#c678dd">if</span> <span style="color:#e06c75">err</span> <span style="color:#56b6c2">!=</span> <span style="color:#e5c07b">nil</span> {
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">4</span><span>        <span style="color:#c678dd">return</span> <span style="color:#e06c75">t</span>, <span style="color:#e06c75">fmt</span>.<span style="color:#61afef;font-weight:bold">Errorf</span>(<span style="color:#98c379">&#34;couldn’t parse %v: %v&#34;</span>, <span style="color:#e06c75">timeStr</span>, <span style="color:#e06c75">err</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">5</span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">6</span><span>    <span style="color:#c678dd">return</span> <span style="color:#e06c75">t</span>, <span style="color:#e5c07b">nil</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">7</span><span>}
</span></span></code></pre></div><p>The key takeaway here is that the <code>return</code> line in the error case (line 4 above) depends on the return type of the function and so can vary significantly.</p>
<p>This is covered in more details in this <a href="https://go.dev/blog/error-handling-and-go">post</a> on the Go blog.</p>
<h2 id="clever">Clever Snippets with LuaSnip and Treesitter</h2>
<p>As we have seen, error handling code varies depending on the number and type of the values returned by the parent function.
So we are very often writing variations of the following in our Go code:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#c678dd">func</span> <span style="color:#61afef;font-weight:bold">f</span>(<span style="color:#e06c75">arg1</span> <span style="color:#e5c07b">string</span>, <span style="color:#e06c75">arg2</span> <span style="color:#e5c07b">string</span>) (…, <span style="color:#e5c07b">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">val</span>, <span style="color:#e06c75">err</span> <span style="color:#56b6c2">:=</span> <span style="color:#61afef;font-weight:bold">someFunction</span>(<span style="color:#e06c75">arg1</span>, <span style="color:#e06c75">arg2</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c678dd">if</span> <span style="color:#e06c75">err</span> <span style="color:#56b6c2">!=</span> <span style="color:#e5c07b">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#c678dd">return</span> …, <span style="color:#e06c75">err</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    …
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Or with 3 return values:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#c678dd">func</span> <span style="color:#61afef;font-weight:bold">f</span>(<span style="color:#e06c75">arg</span> <span style="color:#e5c07b">string</span>) (…, …, <span style="color:#e5c07b">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">val</span>, <span style="color:#e06c75">err</span> <span style="color:#56b6c2">:=</span> <span style="color:#61afef;font-weight:bold">someFunction</span>(<span style="color:#e06c75">arg</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c678dd">if</span> <span style="color:#e06c75">err</span> <span style="color:#56b6c2">!=</span> <span style="color:#e5c07b">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#c678dd">return</span> …, …, <span style="color:#e06c75">err</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    …
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>It would be handy to have a snippet that would adjust the <code>return</code>’s shape depending on the return type of the function that contains it.</p>
<h3 id="demo">Demo</h3>
<p>Here is a short asciicast demonstration<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> of the snippet we are going to build. It is called <code>smart_err</code>:</p>

    <script src="https://cj.rs/js/asciinema-player.min.0a355ec0e0db21622c1aa829f4ffd49e.js"></script>
    <link rel="stylesheet" type="text/css" href="https://cj.rs/css/asciinema-player.min.b76f6c28744857ba1315b54d09acafff.css" /><div id="demo1"></div>
<script>
AsciinemaPlayer.create("/blog/luasnip-and-treesitter-for-smarter-snippets/demo.json", document.getElementById('demo1'), {
"idleTimeLimit":  0.4 ,"loop":  true ,"poster": "npt:0:31","speed":  1.2 ,
});
</script>
<noscript><blockquote><p>To run this asciicast without javascript, use <code>asciinema play https://cj.rs/blog/luasnip-and-treesitter-for-smarter-snippets/demo.json</code> with <a href="https://asciinema.org/">Asciinema</a></p></blockquote></noscript>

<p>The snippet inserted (line 11 in the asciicast) is</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#e06c75">myVal</span>, <span style="color:#e06c75">myErr</span> <span style="color:#56b6c2">:=</span> <span style="color:#61afef;font-weight:bold">anotherFunc</span>(<span style="color:#e06c75">arg1</span>, <span style="color:#e06c75">arg2</span>)
</span></span><span style="display:flex;"><span><span style="color:#c678dd">if</span> <span style="color:#e06c75">myErr</span> <span style="color:#56b6c2">!=</span> <span style="color:#e5c07b">nil</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#c678dd">return</span> <span style="color:#e5c07b">false</span>, <span style="color:#e06c75">fmt</span>.<span style="color:#61afef;font-weight:bold">Errorf</span>(<span style="color:#98c379">&#34;anotherFunc: %v&#34;</span>, <span style="color:#e06c75">myErr</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>when the return type of the function was <code>(bool, error)</code>.</p>
<p>And</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#e06c75">val</span>, <span style="color:#e06c75">err</span> <span style="color:#56b6c2">:=</span> <span style="color:#61afef;font-weight:bold">f</span>()
</span></span><span style="display:flex;"><span><span style="color:#c678dd">if</span> <span style="color:#e06c75">err</span> <span style="color:#56b6c2">!=</span> <span style="color:#e5c07b">nil</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#c678dd">return</span> <span style="color:#e5c07b">false</span>, <span style="color:#e06c75">fmt</span>.<span style="color:#61afef;font-weight:bold">Errorf</span>(<span style="color:#98c379">&#34;f: %v&#34;</span>, <span style="color:#e06c75">err</span>), <span style="color:#d19a66">0</span>, <span style="color:#98c379">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>when the return type was <code>(bool, error, int, string)</code>.</p>
<p>And it even works in nested functions:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#c678dd">func</span> <span style="color:#61afef;font-weight:bold">myFunc</span>() (<span style="color:#56b6c2">*</span><span style="color:#e06c75">MyStruct</span>, <span style="color:#e5c07b">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#7f848e">// ...</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e06c75">a</span> <span style="color:#56b6c2">:=</span> <span style="color:#c678dd">func</span>() (<span style="color:#e5c07b">bool</span>, <span style="color:#e5c07b">error</span>, <span style="color:#e5c07b">int</span>, <span style="color:#e5c07b">string</span>) {
</span></span><span style="display:flex;"><span>		<span style="color:#7f848e">// The snippet detected that we need 4 values here,</span>
</span></span><span style="display:flex;"><span>		<span style="color:#7f848e">// and not 2 as in the outer function</span>
</span></span><span style="display:flex;"><span>		<span style="color:#c678dd">return</span> <span style="color:#e06c75">v</span>, <span style="color:#e5c07b">nil</span>, <span style="color:#d19a66">0</span>, <span style="color:#98c379">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>	}
</span></span></code></pre></div><p>Also note that we can choose to wrap the containing error, or not, with a keystroke (bound to <code>require(&quot;luasnip&quot;).change_choice(1)</code>). The snippet switches between the following 3 forms (line 18 in the asciicast):</p>
<ol>
<li>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#c678dd">return</span> <span style="color:#e5c07b">false</span>, <span style="color:#e06c75">err</span>
</span></span></code></pre></div></li>
<li>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#c678dd">return</span> <span style="color:#e5c07b">false</span>, <span style="color:#e06c75">fmt</span>.<span style="color:#61afef;font-weight:bold">Errorf</span>(<span style="color:#98c379">&#34;f: %v&#34;</span>, <span style="color:#e06c75">err</span>)
</span></span></code></pre></div></li>
<li>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#c678dd">return</span> <span style="color:#e5c07b">false</span>, <span style="color:#e06c75">errors</span>.<span style="color:#61afef;font-weight:bold">Wrap</span>(<span style="color:#e06c75">err</span>, <span style="color:#98c379">&#34;f&#34;</span>)
</span></span></code></pre></div></li>
</ol>
<p>That’s pretty cool! Let’s see how to add it to your Neovim configuration.</p>
<h3 id="code">Code</h3>
<p>The code to define this snippet was initially written by <a href="https://github.com/tjdevries">TJ DeVries</a><sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. I’ve only slightly adapted it so that it fits in one file. I believe the author welcomes this<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>:</p>




  <figure>
    <blockquote cite="https://github.com/tjdevries/config_manager#readme">
      <p>This is TJ&rsquo;s configuration repo. Feel free to use whatever you would like from it! It&rsquo;d be great if you mentioned where it came from if you think it&rsquo;s cool.</p>

    </blockquote>
    
      <figcaption class="blockquote-caption">
        
          <cite style="text-align: right"><a href="https://github.com/tjdevries/config_manager#readme">https://github.com/tjdevries/config_manager#readme</a></cite>
          <br/>
        
        
      </figcaption>
    
  </figure>



<p>Copy this alongside your other LuaSnip snippets, for instance in <a href="./go.lua"><code>~/.config/nvim/snippets/go.lua</code></a>:</p>
<div class="highlight" id="luasnip"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">  1</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">ls</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">require</span> <span style="color:#98c379">&#34;luasnip&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">  2</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">f</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">ls.function_node</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">  3</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">s</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">ls.s</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">  4</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">i</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">ls.insert_node</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">  5</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">t</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">ls.text_node</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">  6</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">d</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">ls.dynamic_node</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">  7</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">c</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">ls.choice_node</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">  8</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">snippet_from_nodes</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">ls.sn</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">  9</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 10</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">ts_locals</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">require</span> <span style="color:#98c379">&#34;nvim-treesitter.locals&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 11</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">ts_utils</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">require</span> <span style="color:#98c379">&#34;nvim-treesitter.ts_utils&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 12</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">get_node_text</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">vim.treesitter</span>.<span style="color:#e06c75">get_node_text</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 13</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 14</span><span><span style="color:#7f848e">-- Adapted from https://github.com/tjdevries/config_manager/blob/1a93f03dfe254b5332b176ae8ec926e69a5d9805/xdg_config/nvim/lua/tj/snips/ft/go.lua</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 15</span><span><span style="color:#c678dd">local</span> <span style="color:#c678dd">function</span> <span style="color:#61afef;font-weight:bold">same</span>(<span style="color:#e06c75">index</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 16</span><span>  <span style="color:#c678dd">return</span> <span style="color:#e06c75">f</span>(<span style="color:#c678dd">function</span>(<span style="color:#e06c75">args</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 17</span><span>    <span style="color:#c678dd">return</span> <span style="color:#e06c75">args</span>[<span style="color:#d19a66">1</span>]
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 18</span><span>  <span style="color:#c678dd">end</span>, { <span style="color:#e06c75">index</span> })
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 19</span><span><span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 20</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 21</span><span><span style="color:#7f848e">-- Adapted from https://github.com/tjdevries/config_manager/blob/1a93f03dfe254b5332b176ae8ec926e69a5d9805/xdg_config/nvim/lua/tj/snips/ft/go.lua</span>
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 22</span><span><span style="color:#e06c75">vim.treesitter</span>.<span style="color:#e06c75">query.set</span>(
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 23</span><span>  <span style="color:#98c379">&#34;go&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 24</span><span>  <span style="color:#98c379">&#34;LuaSnip_Result&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 25</span><span>  <span style="color:#98c379">[[ [
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 26</span><span><span style="color:#98c379">    (method_declaration result: (_) @id)
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 27</span><span><span style="color:#98c379">    (function_declaration result: (_) @id)
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 28</span><span><span style="color:#98c379">    (func_literal result: (_) @id)
</span></span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 29</span><span><span style="color:#98c379">  ] ]]</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 30</span><span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 31</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 32</span><span><span style="color:#7f848e">-- Adapted from https://github.com/tjdevries/config_manager/blob/1a93f03dfe254b5332b176ae8ec926e69a5d9805/xdg_config/nvim/lua/tj/snips/ft/go.lua</span>
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 33</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">transform</span> <span style="color:#56b6c2">=</span> <span style="color:#c678dd">function</span>(<span style="color:#e06c75">text</span>, <span style="color:#e06c75">info</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 34</span><span>  <span style="color:#c678dd">if</span> <span style="color:#e06c75">text</span> <span style="color:#56b6c2">==</span> <span style="color:#98c379">&#34;int&#34;</span> <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 35</span><span>    <span style="color:#c678dd">return</span> <span style="color:#e06c75">t</span> <span style="color:#98c379">&#34;0&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 36</span><span>  <span style="color:#c678dd">elseif</span> <span style="color:#e06c75">text</span> <span style="color:#56b6c2">==</span> <span style="color:#98c379">&#34;error&#34;</span> <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 37</span><span>    <span style="color:#c678dd">if</span> <span style="color:#e06c75">info</span> <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 38</span><span>      <span style="color:#e06c75">info.index</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">info.index</span> <span style="color:#56b6c2">+</span> <span style="color:#d19a66">1</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 39</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 40</span><span>      <span style="color:#c678dd">return</span> <span style="color:#e06c75">c</span>(<span style="color:#e06c75">info.index</span>, {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 41</span><span>        <span style="color:#e06c75">t</span>(<span style="color:#e06c75">string.format</span>(<span style="color:#98c379">&#39;fmt.Errorf(&#34;%s: %%v&#34;, %s)&#39;</span>, <span style="color:#e06c75">info.func_name</span>, <span style="color:#e06c75">info.err_name</span>)),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 42</span><span>        <span style="color:#e06c75">t</span>(<span style="color:#e06c75">info.err_name</span>),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 43</span><span>        <span style="color:#7f848e">-- Be cautious with wrapping, it makes the error part of the API of the</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 44</span><span>        <span style="color:#7f848e">-- function, see https://go.dev/blog/go1.13-errors#whether-to-wrap</span>
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 45</span><span>        <span style="color:#e06c75">t</span>(<span style="color:#e06c75">string.format</span>(<span style="color:#98c379">&#39;fmt.Errorf(&#34;%s: %%w&#34;, %s)&#39;</span>, <span style="color:#e06c75">info.func_name</span>, <span style="color:#e06c75">info.err_name</span>)),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 46</span><span>        <span style="color:#7f848e">-- Old style (pre 1.13, see https://go.dev/blog/go1.13-errors), using</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 47</span><span>        <span style="color:#7f848e">-- https://github.com/pkg/errors</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 48</span><span>        <span style="color:#e06c75">t</span>(<span style="color:#e06c75">string.format</span>(<span style="color:#98c379">&#39;errors.Wrap(%s, &#34;%s&#34;)&#39;</span>, <span style="color:#e06c75">info.err_name</span>, <span style="color:#e06c75">info.func_name</span>)),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 49</span><span>      })
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 50</span><span>    <span style="color:#c678dd">else</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 51</span><span>      <span style="color:#c678dd">return</span> <span style="color:#e06c75">t</span> <span style="color:#98c379">&#34;err&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 52</span><span>    <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 53</span><span>  <span style="color:#c678dd">elseif</span> <span style="color:#e06c75">text</span> <span style="color:#56b6c2">==</span> <span style="color:#98c379">&#34;bool&#34;</span> <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 54</span><span>    <span style="color:#c678dd">return</span> <span style="color:#e06c75">t</span> <span style="color:#98c379">&#34;false&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 55</span><span>  <span style="color:#c678dd">elseif</span> <span style="color:#e06c75">text</span> <span style="color:#56b6c2">==</span> <span style="color:#98c379">&#34;string&#34;</span> <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 56</span><span>    <span style="color:#c678dd">return</span> <span style="color:#e06c75">t</span> <span style="color:#98c379">&#39;&#34;&#34;&#39;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 57</span><span>  <span style="color:#c678dd">elseif</span> <span style="color:#e06c75">string.find</span>(<span style="color:#e06c75">text</span>, <span style="color:#98c379">&#34;*&#34;</span>, <span style="color:#d19a66">1</span>, <span style="color:#e5c07b">true</span>) <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 58</span><span>    <span style="color:#c678dd">return</span> <span style="color:#e06c75">t</span> <span style="color:#98c379">&#34;nil&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 59</span><span>  <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 60</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 61</span><span>  <span style="color:#c678dd">return</span> <span style="color:#e06c75">t</span>(<span style="color:#e06c75">text</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 62</span><span><span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 63</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 64</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">handlers</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 65</span><span>  [<span style="color:#98c379">&#34;parameter_list&#34;</span>] <span style="color:#56b6c2">=</span> <span style="color:#c678dd">function</span>(<span style="color:#e06c75">node</span>, <span style="color:#e06c75">info</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 66</span><span>    <span style="color:#c678dd">local</span> <span style="color:#e06c75">result</span> <span style="color:#56b6c2">=</span> {}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 67</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 68</span><span>    <span style="color:#c678dd">local</span> <span style="color:#e06c75">count</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">node</span>:<span style="color:#e06c75">named_child_count</span>()
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 69</span><span>    <span style="color:#c678dd">for</span> <span style="color:#e06c75">idx</span> <span style="color:#56b6c2">=</span> <span style="color:#d19a66">0</span>, <span style="color:#e06c75">count</span> <span style="color:#56b6c2">-</span> <span style="color:#d19a66">1</span> <span style="color:#c678dd">do</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 70</span><span>      <span style="color:#e06c75">table.insert</span>(<span style="color:#e06c75">result</span>, <span style="color:#e06c75">transform</span>(<span style="color:#e06c75">get_node_text</span>(<span style="color:#e06c75">node</span>:<span style="color:#e06c75">named_child</span>(<span style="color:#e06c75">idx</span>), <span style="color:#d19a66">0</span>), <span style="color:#e06c75">info</span>))
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 71</span><span>      <span style="color:#c678dd">if</span> <span style="color:#e06c75">idx</span> <span style="color:#56b6c2">~=</span> <span style="color:#e06c75">count</span> <span style="color:#56b6c2">-</span> <span style="color:#d19a66">1</span> <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 72</span><span>        <span style="color:#e06c75">table.insert</span>(<span style="color:#e06c75">result</span>, <span style="color:#e06c75">t</span> { <span style="color:#98c379">&#34;, &#34;</span> })
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 73</span><span>      <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 74</span><span>    <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 75</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 76</span><span>    <span style="color:#c678dd">return</span> <span style="color:#e06c75">result</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 77</span><span>  <span style="color:#c678dd">end</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 78</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 79</span><span>  [<span style="color:#98c379">&#34;type_identifier&#34;</span>] <span style="color:#56b6c2">=</span> <span style="color:#c678dd">function</span>(<span style="color:#e06c75">node</span>, <span style="color:#e06c75">info</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 80</span><span>    <span style="color:#c678dd">local</span> <span style="color:#e06c75">text</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">get_node_text</span>(<span style="color:#e06c75">node</span>, <span style="color:#d19a66">0</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 81</span><span>    <span style="color:#c678dd">return</span> { <span style="color:#e06c75">transform</span>(<span style="color:#e06c75">text</span>, <span style="color:#e06c75">info</span>) }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 82</span><span>  <span style="color:#c678dd">end</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 83</span><span>}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 84</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 85</span><span><span style="color:#7f848e">-- Adapted from https://github.com/tjdevries/config_manager/blob/1a93f03dfe254b5332b176ae8ec926e69a5d9805/xdg_config/nvim/lua/tj/snips/ft/go.lua</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 86</span><span><span style="color:#c678dd">local</span> <span style="color:#c678dd">function</span> <span style="color:#61afef;font-weight:bold">go_result_type</span>(<span style="color:#e06c75">info</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 87</span><span>  <span style="color:#c678dd">local</span> <span style="color:#e06c75">cursor_node</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">ts_utils.get_node_at_cursor</span>()
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 88</span><span>  <span style="color:#c678dd">local</span> <span style="color:#e06c75">scope</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">ts_locals.get_scope_tree</span>(<span style="color:#e06c75">cursor_node</span>, <span style="color:#d19a66">0</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 89</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 90</span><span>  <span style="color:#c678dd">local</span> <span style="color:#e06c75">function_node</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 91</span><span>  <span style="color:#c678dd">for</span> <span style="color:#e06c75">_</span>, <span style="color:#e06c75">v</span> <span style="color:#c678dd">in</span> <span style="color:#e06c75">ipairs</span>(<span style="color:#e06c75">scope</span>) <span style="color:#c678dd">do</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 92</span><span>    <span style="color:#c678dd">if</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 93</span><span>      <span style="color:#e06c75">v</span>:<span style="color:#e06c75">type</span>() <span style="color:#56b6c2">==</span> <span style="color:#98c379">&#34;function_declaration&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 94</span><span>      <span style="color:#56b6c2">or</span> <span style="color:#e06c75">v</span>:<span style="color:#e06c75">type</span>() <span style="color:#56b6c2">==</span> <span style="color:#98c379">&#34;method_declaration&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 95</span><span>      <span style="color:#56b6c2">or</span> <span style="color:#e06c75">v</span>:<span style="color:#e06c75">type</span>() <span style="color:#56b6c2">==</span> <span style="color:#98c379">&#34;func_literal&#34;</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 96</span><span>    <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 97</span><span>      <span style="color:#e06c75">function_node</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">v</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 98</span><span>      <span style="color:#c678dd">break</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 99</span><span>    <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">100</span><span>  <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">101</span><span>
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">102</span><span>  <span style="color:#c678dd">local</span> <span style="color:#e06c75">query</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">vim.treesitter</span>.<span style="color:#e06c75">query.get</span>(<span style="color:#98c379">&#34;go&#34;</span>, <span style="color:#98c379">&#34;LuaSnip_Result&#34;</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">103</span><span>  <span style="color:#c678dd">for</span> <span style="color:#e06c75">_</span>, <span style="color:#e06c75">node</span> <span style="color:#c678dd">in</span> <span style="color:#e06c75">query</span>:<span style="color:#e06c75">iter_captures</span>(<span style="color:#e06c75">function_node</span>, <span style="color:#d19a66">0</span>) <span style="color:#c678dd">do</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">104</span><span>    <span style="color:#c678dd">if</span> <span style="color:#e06c75">handlers</span>[<span style="color:#e06c75">node</span>:<span style="color:#e06c75">type</span>()] <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">105</span><span>      <span style="color:#c678dd">return</span> <span style="color:#e06c75">handlers</span>[<span style="color:#e06c75">node</span>:<span style="color:#e06c75">type</span>()](<span style="color:#e06c75">node</span>, <span style="color:#e06c75">info</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">106</span><span>    <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">107</span><span>  <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">108</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">109</span><span>  <span style="color:#c678dd">return</span> { <span style="color:#e06c75">t</span> <span style="color:#98c379">&#34;nil&#34;</span> }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">110</span><span><span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">111</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">112</span><span><span style="color:#7f848e">-- Adapted from https://github.com/tjdevries/config_manager/blob/1a93f03dfe254b5332b176ae8ec926e69a5d9805/xdg_config/nvim/lua/tj/snips/ft/go.lua</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">113</span><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">go_ret_vals</span> <span style="color:#56b6c2">=</span> <span style="color:#c678dd">function</span>(<span style="color:#e06c75">args</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">114</span><span>  <span style="color:#c678dd">return</span> <span style="color:#e06c75">snippet_from_nodes</span>(
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">115</span><span>    <span style="color:#e5c07b">nil</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">116</span><span>    <span style="color:#e06c75">go_result_type</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">117</span><span>      <span style="color:#e06c75">index</span> <span style="color:#56b6c2">=</span> <span style="color:#d19a66">0</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">118</span><span>      <span style="color:#e06c75">err_name</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">args</span>[<span style="color:#d19a66">1</span>][<span style="color:#d19a66">1</span>],
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">119</span><span>      <span style="color:#e06c75">func_name</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">args</span>[<span style="color:#d19a66">2</span>][<span style="color:#d19a66">1</span>],
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">120</span><span>    }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">121</span><span>  )
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">122</span><span><span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">123</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">124</span><span><span style="color:#c678dd">return</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">125</span><span>  <span style="color:#7f848e">-- Adapted from https://github.com/tjdevries/config_manager/blob/1a93f03dfe254b5332b176ae8ec926e69a5d9805/xdg_config/nvim/lua/tj/snips/ft/go.lua</span>
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">126</span><span>  <span style="color:#e06c75">s</span>(<span style="color:#98c379">&#34;smart_err&#34;</span>, {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">127</span><span>    <span style="color:#e06c75">i</span>(<span style="color:#d19a66">1</span>, { <span style="color:#98c379">&#34;val&#34;</span> }),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">128</span><span>    <span style="color:#e06c75">t</span> <span style="color:#98c379">&#34;, &#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">129</span><span>    <span style="color:#e06c75">i</span>(<span style="color:#d19a66">2</span>, { <span style="color:#98c379">&#34;err&#34;</span> }),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">130</span><span>    <span style="color:#e06c75">t</span> <span style="color:#98c379">&#34; := &#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">131</span><span>    <span style="color:#e06c75">i</span>(<span style="color:#d19a66">3</span>, { <span style="color:#98c379">&#34;f&#34;</span> }),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">132</span><span>    <span style="color:#e06c75">t</span> <span style="color:#98c379">&#34;(&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">133</span><span>    <span style="color:#e06c75">i</span>(<span style="color:#d19a66">4</span>),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">134</span><span>    <span style="color:#e06c75">t</span> <span style="color:#98c379">&#34;)&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">135</span><span>    <span style="color:#e06c75">t</span> { <span style="color:#98c379">&#34;&#34;</span>, <span style="color:#98c379">&#34;if &#34;</span> },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">136</span><span>    <span style="color:#e06c75">same</span>(<span style="color:#d19a66">2</span>),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">137</span><span>    <span style="color:#e06c75">t</span> { <span style="color:#98c379">&#34; != nil {&#34;</span>, <span style="color:#98c379">&#34;</span><span style="color:#98c379">\t</span><span style="color:#98c379">return &#34;</span> },
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">138</span><span>    <span style="color:#e06c75">d</span>(<span style="color:#d19a66">5</span>, <span style="color:#e06c75">go_ret_vals</span>, { <span style="color:#d19a66">2</span>, <span style="color:#d19a66">3</span> }),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">139</span><span>    <span style="color:#e06c75">t</span> { <span style="color:#98c379">&#34;&#34;</span>, <span style="color:#98c379">&#34;}&#34;</span> },
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">140</span><span>    <span style="color:#e06c75">i</span>(<span style="color:#d19a66">0</span>),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">141</span><span>  }),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">142</span><span>}
</span></span></code></pre></div><p>We start by importing and aliasing some LuaSnip functions. The snippet itself is defined at the end (line 126). Most of those functions were covered in the <a href="https://cj.rs/blog/generating-snippets-with-luasnip/#3-generating-snippets-variants">previous post</a>, see there for calls to <code>s</code>, <code>i</code> and <code>t</code>. The clever part of this snippet is the <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#dynamicnode">dynamic node</a> <code>d</code> on line 138. There, we call a function that queries (line 22 and 102) Treesitter to find the return types of the current function in Go code. Depending on the types found, default values for the type are used (line 33).</p>
<h3 id="treesitter-query">Treesitter Query</h3>
<p>The Treesitter query is where the magic happens, so let’s take a deeper look.</p>
<p>Treesitter parses code and builds a tree with node for various parts of the code. Each node can have children, like a function definition has a name, a body… This tree can be queried in a Lisp-like language (s-expressions). For instance, in our snippet definition, we use this query:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">1</span><span>[
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">2</span><span>    (<span style="color:#61afef;font-weight:bold">method_declaration</span> <span style="color:#e06c75">result:</span> (<span style="color:#61afef;font-weight:bold">_</span>) <span style="color:#e06c75">@id</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">3</span><span>    (<span style="color:#61afef;font-weight:bold">function_declaration</span> <span style="color:#e06c75">result:</span> (<span style="color:#61afef;font-weight:bold">_</span>) <span style="color:#e06c75">@id</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">4</span><span>    (<span style="color:#61afef;font-weight:bold">func_literal</span> <span style="color:#e06c75">result:</span> (<span style="color:#61afef;font-weight:bold">_</span>) <span style="color:#e06c75">@id</span>)
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">5</span><span>]
</span></span></code></pre></div><p>The <a href="https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries">bracket syntax</a> returns the nodes that match any of the patterns inside the brackets. So it’s effectively the union of all the nodes matched by any of the 3 patterns (on lines 2 to 4). Let’s take a concrete example and put the <a href="https://gist.github.com/cljoly/0b754259df036ba0163f2cdaad48e048#file-main-go">Go code</a> in the <a href="https://tree-sitter.github.io/tree-sitter/playground">Treesitter playground</a>, with a trimmed down version of the query:</p>
<p>
<img alt="The Treesitter playground with the example Go code above inserted at the top, the query in the middle and the tree at the bottom" loading="lazy" src="/blog/luasnip-and-treesitter-for-smarter-snippets/treesitter-playground.png"></p>
<p>The tree built by Treesitter is at the bottom, with the corresponding code at the top and the query in the middle. The <code>function_declaration</code> node is highlighted in gray in the tree, like the corresponding part of the code (from line 13 to line 22). This node has a number of children, with the function <code>name</code>, the <code>parameters</code> of the function, a <code>result</code> node for the return type and <code>body</code> for the body of the function between curly braces.</p>
<p>Taking the first pattern on the playground, slightly modified:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>    (<span style="color:#61afef;font-weight:bold">function_declaration</span> <span style="color:#e06c75">result:</span> (<span style="color:#61afef;font-weight:bold">_</span>) <span style="color:#e06c75">@id2</span>)
</span></span></code></pre></div><p>The part it captures is highlighted in blue in the Go code: <code>(*MyStruct, error)</code>. It makes sense, since the <code>function_declaration</code> (the part on the gray background) has a <code>result</code> child, that’s captured by <code>@id</code>.</p>
<p>Now, on to the second pattern:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>    (<span style="color:#61afef;font-weight:bold">func_literal</span> <span style="color:#e06c75">result:</span> (<span style="color:#61afef;font-weight:bold">_</span>) <span style="color:#e06c75">@id3</span>)
</span></span></code></pre></div><p>Similarly, further down in the <code>body</code> of that function, the return type <code>(bool, error, int, string)</code> of the anonymous function is matched.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I started writing this post over a year ago. The snippet has been very stable and kept working as without changes, which is a testament to the stability of the LuaSnip (and Treesitter) APIs. I’m still using it when I write Go code, even if some corner cases (like custom structs) aren’t handled. The snippet is quite complicated and niche, so I’m not sure that I would have invested the time to write it myself. I probably don’t write enough Go code to <a href="https://xkcd.com/1205/">justify the investment</a>, but finding it shared by someone buried in their config file made it worth it.
I hope you have found this port useful, either to copy this particular snippet or as an example of advanced snippets.</p>
<p>This is the last post in a <a href="https://cj.rs/tags/luasnip/">series on LuaSnip</a>.</p>
<h2 id="resources">Resources</h2>
<p>If you want to dig deeper:</p>
<ul>
<li><a href="https://go.dev/blog/go1.13-errors">Working with Errors in Go 1.13</a></li>
<li><a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md">LuaSnip documentation</a></li>
<li>Explore the Treesitter tree for any Neovim buffer where it is active with <code>:lua vim.treesitter.inspect_tree()</code></li>
<li>Treesitter’s <a href="https://tree-sitter.github.io/tree-sitter/using-parsers#pattern-matching-with-queries">Pattern Matching with Queries</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>The test code is available as <a href="https://gist.github.com/cljoly/0b754259df036ba0163f2cdaad48e048#file-main-go">gist</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>By the way, he has a very interesting <a href="https://www.twitch.tv/teej_dv">Twitch channel</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>At the time of writing at least. See <a href="https://perma.cc/XZW9-B2UA">perma.cc</a> and <a href="https://web.archive.org/web/20221202063059/https://github.com/tjdevries/config_manager/blob/master/README.md">the wayback machine</a> for a snapshot.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>Generating Snippets with LuaSnip in Neovim</title><link>https://cj.rs/blog/generating-snippets-with-luasnip/</link><pubDate>Sun, 31 Jul 2022 22:10:49 +0100</pubDate><guid>https://cj.rs/blog/generating-snippets-with-luasnip/</guid><description>Generating repetitive snippets with code, like time variant of section titles.</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-tldr">
    <p class="alert-heading">
      ⚡
      
        TL;DR
      
    </p>
    <p>When you have many variations of the same snippet, one option is to generate those with Lua code. The complete example is <a href="#the-final-code">at the end</a>.</p>
  </div>



<p>I’ve <a href="https://cj.rs/blog/ultisnips-to-luasnip/">recently moved</a> to <a href="https://github.com/L3MON4D3/LuaSnip">LuaSnip</a> as my snippets plugin for Neovim.
When I first started, I sticked to the simplest features of LuaSnip, in particular the SnipMate-like syntax for snippets.
But I have now started to explore the more distinctive features of LuaSnip, like Lua-defined snippets.
It turns out that generating snippets with code can save tedious repetition.</p>
<h2 id="markdown-journaling">Markdown Journaling</h2>
<p>I tend to take notes in Markdown documents.
I usually set up one text file for each recurring themes or project.
Then, there is a “h1” title per day, a bit like the <a href="https://help.bulletjournal.com/article/27-daily-log">Bullet Journal daily log</a>.
The resulting file looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-markdown" data-lang="markdown"><span style="display:flex; background-color:#3d4148"><span># 2022-07-31 Sun
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#c678dd">*</span> Point 1
</span></span><span style="display:flex;"><span><span style="color:#c678dd">*</span> Point 2
</span></span><span style="display:flex;"><span><span style="color:#c678dd">*</span> Point 3
</span></span></code></pre></div><p>I have a snippet to generate the <code># 2022-07-31 Sun</code> line, based on the current date and day of the week, as I explained <a href="https://cj.rs/blog/ultisnips-to-luasnip/#advanced-snippets-with-environment-variables">last time</a>.
For reference, here is the snippet from back then:</p>
<pre tabindex="0"><code class="language-snippet" data-lang="snippet"># “Bullet Journal”-styled date for today
snippet bjtoday
  # ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} $CURRENT_DAY_NAME_SHORT
</code></pre><h2 id="lua-generated-snippets">Lua-Generated Snippets</h2>
<p>But then, I also sometimes want to put notes for the next session.
I usually know when that will be, because the meeting happens, say, every Thursday or every month.
So it would be great to have snippets for those dates as well.
Let’s build them step by step.</p>
<h3 id="1-computing-the-date-of-next-friday">1. Computing the Date of “Next Friday”</h3>
<p>The first step is to calculate the date and weekday from human concepts like “next Friday” or “next week”.
Thankfully <a href="https://www.gnu.org/software/coreutils/manual/html_node/date-invocation.html#date-invocation">GNU date</a> supports this, and it’s preinstalled on most GNU/Linux systems:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>$ date -d <span style="color:#98c379">&#39;next Friday&#39;</span> +<span style="color:#98c379">&#39;%F %a&#39;</span>
</span></span><span style="display:flex;"><span>2022-08-05 Fri
</span></span></code></pre></div><h3 id="2-putting-those-dates-in-luasnip">2. Putting Those Dates in LuaSnip</h3>
<p>The second step is to use the output of the <code>date</code> command in LuaSnip.</p>
<p>We could <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#variables">define our own variables</a> like <code>${NEXT_MONDAY_DATE}</code>.
That would be similar to the <code>${CURRENT_DATE}</code> we used before.
But we would need many such variables, for the date and weekday of the next Monday, Tuesday, …, next week or next month.
Plus I use those dates only in Markdown snippets, so it feels a bit wasteful to define those variables everywhere.</p>
<p>In the end, I chose to use <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#functionnode">function nodes</a> to call the <code>date</code> command on the fly, when the snippet gets expanded.
This node is inserted in a particular snippet definition and is thus contained to that particular snippet.
This way, we avoid creating values everywhere, and we keep the global namespace clean.
It looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">f</span>(<span style="color:#c678dd">function</span>(<span style="color:#e06c75">args</span>, <span style="color:#e06c75">snip</span>, <span style="color:#e06c75">user_arg_1</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#c678dd">return</span> <span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">trim</span>(<span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">system</span>(<span style="color:#98c379">[[date -d &#39;]]</span> <span style="color:#56b6c2">..</span> <span style="color:#e06c75">target_date</span> <span style="color:#56b6c2">..</span> <span style="color:#98c379">[[&#39; +&#39;%F %a&#39;]]</span>))
</span></span><span style="display:flex;"><span><span style="color:#c678dd">end</span>, {}),
</span></span></code></pre></div><h3 id="3-generating-snippets-variants">3. Generating Snippets Variants</h3>
<p>The last step is to generate the variants of the base snippet: <code>bj_today</code>, <code>bj_next_monday</code>, …, <code>bj_next_week</code> and <code>bj_next_month</code>.</p>
<p>In LuaSnip, snippets defined using Lua are just a table calling some agreed-upon functions.
Here is an example from the <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md#basics">documentation</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">ls.add_snippets</span>(<span style="color:#98c379">&#34;all&#34;</span>, {
</span></span><span style="display:flex;"><span>	<span style="color:#e06c75">s</span>(<span style="color:#98c379">&#34;ternary&#34;</span>, {
</span></span><span style="display:flex;"><span>		<span style="color:#7f848e">-- equivalent to &#34;${1:cond} ? ${2:then} : ${3:else}&#34;</span>
</span></span><span style="display:flex;"><span>		<span style="color:#e06c75">i</span>(<span style="color:#d19a66">1</span>, <span style="color:#98c379">&#34;cond&#34;</span>), <span style="color:#e06c75">t</span>(<span style="color:#98c379">&#34; ? &#34;</span>), <span style="color:#e06c75">i</span>(<span style="color:#d19a66">2</span>, <span style="color:#98c379">&#34;then&#34;</span>), <span style="color:#e06c75">t</span>(<span style="color:#98c379">&#34; : &#34;</span>), <span style="color:#e06c75">i</span>(<span style="color:#d19a66">3</span>, <span style="color:#98c379">&#34;else&#34;</span>)
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>This creates a snippet (the <code>s</code> function) for <code>all</code> file types. This snippet will expand <code>ternary</code> into <code>cond ? then : else</code>, prompting the user to change the <code>cond</code>, <code>then</code> and <code>else</code> bits (function <code>i</code>) while <code>?</code> and <code>:</code> are “static text” (function <code>t</code>).</p>
<p>We want to write a function that builds a table of date snippet definitions.
That will be returned when the Lua module is loaded.</p>
<h4 id="the-final-code">The Final Code</h4>
<p>The final snippet looks like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 1</span><span><span style="color:#7f848e">-- Bullet Journal styled dates in the future</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 2</span><span><span style="color:#c678dd">local</span> <span style="color:#c678dd">function</span> <span style="color:#61afef;font-weight:bold">gen_date_snippets</span>()
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 3</span><span>  <span style="color:#c678dd">local</span> <span style="color:#e06c75">snippets</span> <span style="color:#56b6c2">=</span> {}
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 4</span><span>  <span style="color:#c678dd">local</span> <span style="color:#e06c75">target_dates</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 5</span><span>    <span style="color:#98c379">&#34;today&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 6</span><span>    <span style="color:#98c379">&#34;tomorrow&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 7</span><span>    <span style="color:#98c379">&#34;next monday&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 8</span><span>    <span style="color:#98c379">&#34;next tuesday&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f"> 9</span><span>    <span style="color:#98c379">&#34;next wednesday&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">10</span><span>    <span style="color:#98c379">&#34;next thursday&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">11</span><span>    <span style="color:#98c379">&#34;next friday&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">12</span><span>    <span style="color:#98c379">&#34;next week&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">13</span><span>    <span style="color:#98c379">&#34;next month&#34;</span>,
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">14</span><span>  }
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">15</span><span>  <span style="color:#c678dd">for</span> <span style="color:#e06c75">_</span>, <span style="color:#e06c75">target_date</span> <span style="color:#c678dd">in</span> <span style="color:#e06c75">pairs</span>(<span style="color:#e06c75">target_dates</span>) <span style="color:#c678dd">do</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">16</span><span>    <span style="color:#e06c75">table.insert</span>(
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">17</span><span>      <span style="color:#e06c75">snippets</span>,
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">18</span><span>      <span style="color:#e06c75">s</span>(<span style="color:#98c379">&#34;bj_&#34;</span> <span style="color:#56b6c2">..</span> <span style="color:#e06c75">target_date</span>:<span style="color:#e06c75">gsub</span>(<span style="color:#98c379">&#34; &#34;</span>, <span style="color:#98c379">&#34;_&#34;</span>), {
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">19</span><span>        <span style="color:#e06c75">t</span> <span style="color:#98c379">&#34;# &#34;</span>,
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">20</span><span>        <span style="color:#e06c75">f</span>(<span style="color:#c678dd">function</span>(<span style="color:#e06c75">args</span>, <span style="color:#e06c75">snip</span>, <span style="color:#e06c75">user_arg_1</span>)
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">21</span><span>          <span style="color:#c678dd">return</span> <span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">trim</span>(<span style="color:#e06c75">vim.fn</span>.<span style="color:#e06c75">system</span>(<span style="color:#98c379">[[date -d &#39;]]</span> <span style="color:#56b6c2">..</span> <span style="color:#e06c75">target_date</span> <span style="color:#56b6c2">..</span> <span style="color:#98c379">[[&#39; +&#39;%F %a&#39;]]</span>))
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">22</span><span>        <span style="color:#c678dd">end</span>, {}),
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">23</span><span>      })
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">24</span><span>    )
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">25</span><span>  <span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">26</span><span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">27</span><span>  <span style="color:#c678dd">return</span> <span style="color:#e06c75">snippets</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">28</span><span><span style="color:#c678dd">end</span>
</span></span><span style="display:flex;"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">29</span><span>
</span></span><span style="display:flex; background-color:#3d4148"><span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#55595f">30</span><span><span style="color:#c678dd">return</span> <span style="color:#e06c75">gen_date_snippets</span>()
</span></span></code></pre></div><p>This generates the snippets name (line 18), for instance <code>bj_today</code> or <code>bj_next_tuesday</code> from the arguments passed to the <code>date</code> command.
The body of the snippet is made of text (<code>t &quot;# &quot;</code>) and our function node from earlier (line 20-22).
All those snippets get collected in a table that is returned at the end of the Lua module (line 30).
It’s then usable by LuaSnip.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Generating a bunch of similar snippets like this turned out to be an unanticipated benefit of using Lua to define snippets.
Before using LuaSnip, I only had <code>bj_today</code>, <code>bj_tomorrow</code> and <code>bj_next_monday</code>.
If I had wanted to expand this set further, I would have to do some more copying and pasting, which quickly becomes unmaintainable.</p>
<p>Of course for simpler or less repetitive snippets, it’s probably best to use the less verbose SnipMate-like syntax presented in the <a href="https://cj.rs/blog/ultisnips-to-luasnip/">previous post</a>.</p>
]]></content:encoded></item><item><title>From UltiSnips to LuaSnip</title><link>https://cj.rs/blog/ultisnips-to-luasnip/</link><pubDate>Sun, 15 May 2022 07:06:49 +0100</pubDate><guid>https://cj.rs/blog/ultisnips-to-luasnip/</guid><description>Shaving off a few milliseconds from Neovim startup time.</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-tldr">
    <p class="alert-heading">
      ⚡
      
        TL;DR
      
    </p>
    <p><a href="https://github.com/L3MON4D3/LuaSnip">LuaSnip</a> is fast and doesn’t have to be complicated. Give it a try!</p>
  </div>







  
  
  
    
    
  
  

  <details class="alert alert-note">
    <summary class="alert-heading">
      ℹ️
      
        About UltiSnips
      
    </summary>
    <p>Even if that article shows how <a href="https://github.com/L3MON4D3/LuaSnip">LuaSnip</a> shines, I have great respect for the work that has gone into <a href="https://github.com/sirver/UltiSnips">UltiSnips</a>. It is still a reliable, reasonably fast plugin given the constraint it operates in (in particular, Vim compatibility requires a fair amount of Vimscript). I’ve written this article shortly after trying LuaSnip and I’m still very much evaluating it.</p>
  </details>



<h2 id="introduction">Introduction</h2>
<p><a href="https://en.wikipedia.org/wiki/Snippet_(programming)">Snippets</a> are a convenient feature of some text editors to insert and adapt reusable pieces of code. For instance, snippets for <code>for</code> loops are common, to get the tedious bits of the syntax out of the way.</p>
<p>To get this feature in Vim back in the days, I started using <a href="https://github.com/sirver/UltiSnips">UltiSnips</a>. There are <a href="https://github.com/honza/vim-snippets">default snippets sets</a> for it, and it’s easy to write custom snippets. These custom snippet can call Bash or Python scripts if you need more dynamic replacements. UltiSnips has been very powerful and has served me quite well over the past decade or so, and I have kept it when I migrated to NeoVim a few years ago.</p>
<h2 id="startup-time">Startup time</h2>
<p>Every once in a while though, I run the excellent <a href="https://github.com/rhysd/vim-startuptime">vim-startuptime</a> command to assess the impact of various configurations and plugins on the startup time of Neovim. With UltiSnips and the corresponding completion plugins in my configuration, the first few lines are:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>Extra options: []
</span></span><span style="display:flex;"><span>Measured: 10 times
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Total Average: 104.218300 msec
</span></span><span style="display:flex;"><span>Total Max:     109.719000 msec
</span></span><span style="display:flex;"><span>Total Min:     99.533000 msec
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  AVERAGE       MAX       MIN
</span></span><span style="display:flex;"><span>------------------------------
</span></span><span style="display:flex; background-color:#3d4148"><span>64.218600 70.419000 61.066000: ~/.config/nvim/init.lua
</span></span><span style="display:flex;"><span> 6.255900  7.238000  5.764000: loading packages
</span></span><span style="display:flex;"><span> 3.939000  5.338000  3.533000: ~/.local/share/nvim/site/pack/paqs/start/onedark.nvim/colors/onedark.lua
</span></span><span style="display:flex;"><span> 3.651100  4.003000  3.381000: loading rtp plugins
</span></span><span style="display:flex;"><span> 2.761600  3.957000  2.474000: expanding arguments
</span></span><span style="display:flex;"><span> 2.683900  3.035000  2.566000: reading ShaDa
</span></span><span style="display:flex;"><span> 2.418700  3.610000  2.131000: sourcing vimrc file(s)
</span></span><span style="display:flex;"><span> 2.389600  2.751000  2.228000: /usr/share/nvim/runtime/filetype.lua
</span></span><span style="display:flex;"><span> 1.954900  2.293000  1.702000: loading after plugins
</span></span><span style="display:flex;"><span> 1.842500  2.015000  1.763000: BufEnter autocommands
</span></span><span style="display:flex; background-color:#3d4148"><span> 1.583350  3.532000  0.018000: ~/.local/share/nvim/site/pack/paqs/start/ultisnips/plugin/UltiSnips.vim
</span></span><span style="display:flex;"><span> 1.027700  1.170000  0.831000: ~/.local/share/nvim/site/pack/paqs/start/nvim-treesitter/plugin/nvim-treesitter.lua
</span></span><span style="display:flex; background-color:#3d4148"><span> 0.968200  1.071000  0.860000: ~/.local/share/nvim/site/pack/paqs/start/cmp-nvim-ultisnips/after/plugin/cmp_nvim_ultisnips.lua
</span></span><span style="display:flex;"><span> 0.859000  1.014000  0.702000: ~/.local/share/nvim/site/pack/paqs/start/nvim-treesitter-textobjects/plugin/nvim-treesitter-textobjects.vim
</span></span><span style="display:flex;"><span> 0.590700  0.674000  0.524000: ~/.local/share/nvim/site/pack/paqs/start/indent-blankline.nvim/plugin/indent_blankline.vim
</span></span><span style="display:flex;"><span> 0.557200  0.817000  0.493000: init highlight
</span></span><span style="display:flex;"><span> 0.553200  0.898000  0.322000: opening buffers</span></span></code></pre></div>
<p>The <code>init.lua</code> line covers a lot of different plugins and mappings set up in that file. Besides the <a href="https://github.com/navarasu/onedark.nvim">onedark.nvim</a> colorscheme<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, the next biggest contributor is <code>~/…/ultisnips/plugin/UltiSnips.vim</code>. The script to integrate UltiSnips with <a href="https://github.com/hrsh7th/nvim-cmp">cmp</a>, <code>~/…/cmp-nvim-ultisnips/after/plugin/cmp_nvim_ultisnips.lua</code>, is not far behind. In total, we spend nearly 4 ms of the startup time for UltiSnips-related files, on top of the setup done in <code>init.lua</code>. That feels suboptimal, so I’ve been looking for a possible snippet plugin alternative.</p>
<h2 id="installing-luasnip">Installing LuaSnip</h2>
<p><a href="https://github.com/L3MON4D3/LuaSnip">LuaSnip</a> aims to be a faster<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> snippet engine, with support of treesitter in the snippets. It can also understand the LSP snippet “format”. Finally, it’s a pure Lua plugin, without any Python requirement, contrary to UltiSnips. That makes installation on various system much easier.</p>
<p>Its drawbacks for me are that it does not support the same snippet definition as UltiSnips and even in its own SnipMate-like syntax, backticks to execute code are not supported. As a result, I’ll have to migrate my (admittedly very small) snippet collection. Conversely, if I want to do more advanced things, I’ll have to learn the relatively complex “VS Studio Code” snippets in JSON or even the pure Lua snippets.</p>
<p>That’s said, I believe I can get the LuaSnip benefits without writing Lua snippets or JSON ones, at least to start. So let’s try and do that, using only the SnipMate-like syntax!</p>
<h2 id="overview-of-the-configuration-changes">Overview of the Configuration Changes</h2>
<p>I’ve made the following changes.</p>
<ol>
<li>Remove UltiSnips and its associated plugins, namely for completion with <a href="https://github.com/hrsh7th/nvim-cmp">cmp</a> and the telescope integration.</li>
<li>Remove the UltiSnips mappings.</li>
<li>Add LuaSnip (following the instructions in the <a href="https://github.com/L3MON4D3/LuaSnip">Readme</a>), <a href="https://github.com/saadparwaiz1/cmp_luasnip">its cmp integration</a>, a <a href="https://github.com/rafamadriz/friendly-snippets">set of snippets</a> and a <a href="https://github.com/benfowler/telescope-luasnip.nvim">telescope integration</a> along with some mappings (requires nvim 0.7+<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>):
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">vim.keymap</span>.<span style="color:#e06c75">set</span>({ <span style="color:#98c379">&#34;i&#34;</span>, <span style="color:#98c379">&#34;s&#34;</span> }, <span style="color:#98c379">&#34;&lt;C-i&gt;&#34;</span>, <span style="color:#c678dd">function</span>() <span style="color:#e06c75">require</span><span style="color:#98c379">&#39;luasnip&#39;</span>.<span style="color:#e06c75">jump</span>(<span style="color:#d19a66">1</span>) <span style="color:#c678dd">end</span>, { <span style="color:#e06c75">desc</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;LuaSnip forward jump&#34;</span> })
</span></span><span style="display:flex;"><span><span style="color:#e06c75">vim.keymap</span>.<span style="color:#e06c75">se</span>({ <span style="color:#98c379">&#34;i&#34;</span>, <span style="color:#98c379">&#34;s&#34;</span> }, <span style="color:#98c379">&#34;&lt;M-i&gt;&#34;</span>, <span style="color:#c678dd">function</span>() <span style="color:#e06c75">require</span><span style="color:#98c379">&#39;luasnip&#39;</span>.<span style="color:#e06c75">jump</span>(<span style="color:#56b6c2">-</span><span style="color:#d19a66">1</span>) <span style="color:#c678dd">end</span>, { <span style="color:#e06c75">desc</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;LuaSnip backward jump&#34;</span> })
</span></span></code></pre></div></li>
<li><a href="#migrate-my-snippet-collection">Migrate my existing snippets</a>, see below.</li>
<li>Add code to load the snippet set and my own snippet collection:
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;luasnip.loaders.from_vscode&#34;</span>).<span style="color:#e06c75">lazy_load</span>()
</span></span><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;luasnip.loaders.from_snipmate&#34;</span>).<span style="color:#e06c75">lazy_load</span>({ <span style="color:#e06c75">paths</span> <span style="color:#56b6c2">=</span> {<span style="color:#98c379">&#34;./snippets&#34;</span>} })
</span></span></code></pre></div></li>
</ol>
<h3 id="after">After</h3>
<p>Let’s run vim-startuptime again. Here are the new top contributors:
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;display:grid;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>Extra options: []
</span></span><span style="display:flex;"><span>Measured: 10 times
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Total Average: 98.251400 msec
</span></span><span style="display:flex;"><span>Total Max:     100.159000 msec
</span></span><span style="display:flex;"><span>Total Min:     96.519000 msec
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  AVERAGE       MAX       MIN
</span></span><span style="display:flex;"><span>------------------------------
</span></span><span style="display:flex; background-color:#3d4148"><span>60.469100 62.341000 59.619000: ~/.config/nvim/init.lua
</span></span><span style="display:flex;"><span> 5.991100  6.219000  5.865000: loading packages
</span></span><span style="display:flex;"><span> 4.088400  4.162000  3.942000: loading after plugins
</span></span><span style="display:flex;"><span> 3.635700  3.752000  3.415000: loading rtp plugins
</span></span><span style="display:flex;"><span> 3.561800  3.936000  3.220000: ~/.local/share/nvim/site/pack/paqs/start/onedark.nvim/colors/onedark.lua
</span></span><span style="display:flex;"><span> 2.550500  2.628000  2.492000: reading ShaDa
</span></span><span style="display:flex;"><span> 2.454200  2.525000  2.401000: expanding arguments
</span></span><span style="display:flex;"><span> 2.363000  2.420000  2.270000: /usr/share/nvim/runtime/filetype.lua
</span></span><span style="display:flex;"><span> 2.271100  2.337000  2.146000: sourcing vimrc file(s)
</span></span><span style="display:flex;"><span> 1.701000  1.767000  1.635000: BufEnter autocommands
</span></span><span style="display:flex;"><span> 1.676700  2.049000  1.366000: opening buffers
</span></span><span style="display:flex;"><span> 1.034100  1.280000  0.782000: ~/.local/share/nvim/site/pack/paqs/start/nvim-treesitter/plugin/nvim-treesitter.lua
</span></span><span style="display:flex;"><span> 0.901000  1.021000  0.799000: ~/.local/share/nvim/site/pack/paqs/start/nvim-treesitter-textobjects/plugin/nvim-treesitter-textobjects.vim
</span></span><span style="display:flex;"><span> 0.590700  0.674000  0.524000: ~/.local/share/nvim/site/pack/paqs/start/indent-blankline.nvim/plugin/indent_blankline.vim
</span></span><span style="display:flex;"><span> 0.549500  0.789000  0.514000: init highlight</span></span></code></pre></div></p>
<p>The snippet infrastructure is now absent from that list! More importantly, the overall startup time is down, and we can be confident that the new calls to load the snippets in <code>init.lua</code> are not costlier than UltiSnips settings before, because the <code>init.lua</code> line is down as well.</p>
<p>As a bonus, the snippet expansion feels slightly snappier with LuaSnip, although it might be an illusion and I don’t have hard numbers to back this claim.</p>
<h2 id="migrate-my-snippet-collection">Migrate my Snippet Collection</h2>
<p>Back to the topic of migrating existing UltiSnips snippets: LuaSnip will loudly complain when given UltiSnips snippets but it’s relatively easy to rewrite those snippets to the SnipMate-like format that LuaSnip understands.</p>
<h3 id="two-syntaxes">Two Syntaxes</h3>
<p>On the one hand, UltiSnips snippets roughly follow this syntax</p>
<pre tabindex="0"><code class="language-snippet" data-lang="snippet">snippet trigger &#34;Comment&#34; option
snippet content
endsnippet
</code></pre><p>And so, my markdown snippets would look like this:</p>
<pre tabindex="0"><code class="language-snippet" data-lang="snippet">priority 10

snippet bjtoday &#34;“Bullet Journal”-styled date for today&#34; b
# `date +&#39;%F %A&#39;`
endsnippet
</code></pre><p>On the other hand, LuaSnip uses a simplified version of SnipMate snippets:</p>
<pre tabindex="0"><code># Comment
snippet toggle
  snippet content
</code></pre><h3 id="simple-snippets">Simple Snippets</h3>
<p>Since I heavily rely on snippet sets, I have only about 30 snippets defined in my own snippet collection. Most of them are really simple, with only a few lines and almost no interactive text. So for most of those, the process has been to simply remove the <code>priority …</code> lines and then a simple substitution command<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> did the trick.</p>
<h3 id="advanced-snippets-with-environment-variables">Advanced Snippets With Environment Variables</h3>
<p>However, there is also the case of the snippets calling external commands, like <code>date</code> in the example below:</p>
<pre tabindex="0"><code class="language-snippet" data-lang="snippet">snippet bjtoday &#34;“Bullet Journal”-styled date for today&#34; b
# `date +&#39;%F %A&#39;`
endsnippet
</code></pre><p>The problem is, to call arbitrary commands, one needs to define the snippets in Lua.</p>
<p>It turns out though that nearly all my snippets calling external command were actually inserting a date. And luckily LuaSnip defines “environment variables” holding just the values I need like <code>$CURRENT_MONTH</code>. So that snippet becomes:</p>
<pre tabindex="0"><code class="language-snippet" data-lang="snippet"># “Bullet Journal”-styled date for today
snippet bjtoday
  # ${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE} $CURRENT_DAY_NAME_SHORT
</code></pre><p>and we get to keep the simple SnipMate-like syntax!</p>
<p>You can find more of those environment variables in <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/lua/luasnip/util/environ.lua">the sources</a>.</p>




  
  
  
  

  <div class="alert alert-edit">
    <p class="alert-heading">
      ✏
      
        Edit
      
    </p>
    <p>2022-08-03: See also <a href="https://github.com/smjonas/snippet-converter.nvim">snippet-converter.nvim</a> to convert between various snippet formats.<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup></p>
  </div>



<h2 id="whats-next">What’s Next</h2>
<p>I now have a slightly faster NeoVim and the snippet syntax that I use the most is simpler. However, there is more to explore!</p>
<p>So far, I’ve steered clear of writing more complicated JSON and Lua snippets. The latter would be necessary to unlock smart snippets using treesitter <a href="https://changelog.com/podcast/457#t=01:00:01.17">context</a>. I’ll look into that next, in particular to generate go error handling code.</p>
<p>This is also a very simple configuration, the impact on startup time of LuaSnip might go up slightly as we use more advanced feature, but during my tests, even using all available features, it was much lighter than UltiSnips.</p>




  
  
  
  

  <div class="alert alert-edit">
    <p class="alert-heading">
      ✏
      
        Edit
      
    </p>
    <p>2022-07-31: <a href="https://cj.rs/tags/luasnip/">Follow-up posts</a> dig deeper in other aspects of LuaSnip</p>
  </div>



<h2 id="resources">Resources</h2>
<ul>
<li>Many more details are covered in <a href="https://github.com/L3MON4D3/LuaSnip/blob/master/DOC.md">LuaSnip documentation</a>.</li>
<li>The <a href="https://github.com/garbas/vim-snipmate/blob/master/doc/SnipMate.txt">SnipMate help</a> contains the SnipMate snippet syntax, if you are unfamiliar with it.</li>
<li>The <a href="https://github.com/microsoft/language-server-protocol/blob/main/snippetSyntax.md">LSP snippet documentation</a> is also helpful.</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>That run is actually on a fork of <a href="https://github.com/navarasu/onedark.nvim">onedark.nvim</a> that contains a massive speed up. The changes of that fork are being up streamed in <a href="https://github.com/navarasu/onedark.nvim/pull/76">this PR</a>.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>According to that comment from the author for instance: <a href="https://github.com/L3MON4D3/LuaSnip/issues/60#issuecomment-873630664">https://github.com/L3MON4D3/LuaSnip/issues/60#issuecomment-873630664</a>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>This code snippet uses new APIs introduced in Neovim 0.7 and the newly freed <code>&lt;C-i&gt;</code>, see <a href="https://gpanders.com/blog/whats-new-in-neovim-0-7/">https://gpanders.com/blog/whats-new-in-neovim-0-7/</a> for more details.&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>For instance: <code>%s/^\(snippet \+\S\+\) &quot;\(.*\)&quot; \w\+\n\(\_.\{-}\)endsnippet/# \2^M\1^M \3^M</code>.&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Thanks to <a href="https://www.reddit.com/user/Miserable-Ad-7341">Miserable-Ad-7341</a> for <a href="https://www.reddit.com/r/neovim/comments/weonip/from_ultisnips_to_luasnip/iipheov/">pointing this out</a>.&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>bepo.nvim</title><link>https://cj.rs/bepo-nvim/</link><pubDate>Mon, 21 Feb 2022 10:02:37 +0000</pubDate><guid>https://cj.rs/bepo-nvim/</guid><description>⌨️ Mappings for the bepo layout in Lua</description><content:encoded><![CDATA[
<p style="display: flex; justify-content: space-between">
  <a href="https://github.com/cljoly/bepo.nvim" data-goatcounter-click="ext-github-bepo.nvim" data-goatcounter-title="cljoly/bepo.nvim">
    <span class="svgicon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
    stroke-linecap="round" stroke-linejoin="round">
    <path
        d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22">
    </path>
</svg></span>&nbsp;cljoly/bepo.nvim
  </a>
  <a class="badges" href="https://github.com/cljoly/bepo-nvim" data-goatcounter-click="ext-stargithub-bepo.nvim" data-goatcounter-title="stars cljoly/bepo.nvim">
    <img src="https://img.shields.io/github/stars/cljoly/bepo.nvim?style=social" alt="Github stars for bepo.nvim">
  </a>
</p>


<div class="badges">

<p>

  <img alt="Neovim version" loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI5NSIgaGVpZ2h0PSIyMCIgcm9sZT0iaW1nIiBhcmlhLWxhYmVsPSJOZW92aW06IDAuNSI+PHRpdGxlPk5lb3ZpbTogMC41PC90aXRsZT48bGluZWFyR3JhZGllbnQgaWQ9InMiIHgyPSIwIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjYmJiIiBzdG9wLW9wYWNpdHk9Ii4xIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xIi8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9InIiPjxyZWN0IHdpZHRoPSI5NSIgaGVpZ2h0PSIyMCIgcng9IjMiIGZpbGw9IiNmZmYiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNyKSI+PHJlY3Qgd2lkdGg9IjY4IiBoZWlnaHQ9IjIwIiBmaWxsPSIjNTU1Ii8+PHJlY3QgeD0iNjgiIHdpZHRoPSIyNyIgaGVpZ2h0PSIyMCIgZmlsbD0iIzU3YTE0MyIvPjxyZWN0IHdpZHRoPSI5NSIgaGVpZ2h0PSIyMCIgZmlsbD0idXJsKCNzKSIvPjwvZz48ZyBmaWxsPSIjZmZmIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0iVmVyZGFuYSxHZW5ldmEsRGVqYVZ1IFNhbnMsc2Fucy1zZXJpZiIgdGV4dC1yZW5kZXJpbmc9Imdlb21ldHJpY1ByZWNpc2lvbiIgZm9udC1zaXplPSIxMTAiPjxpbWFnZSB4PSI1IiB5PSIzIiB3aWR0aD0iMTQiIGhlaWdodD0iMTQiIGhyZWY9ImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsUEhOMlp5Qm1hV3hzUFNJak5UZEJNVFF6SWlCeWIyeGxQU0pwYldjaUlIWnBaWGRDYjNnOUlqQWdNQ0F5TkNBeU5DSWdlRzFzYm5NOUltaDBkSEE2THk5M2QzY3Vkek11YjNKbkx6SXdNREF2YzNabklqNDhkR2wwYkdVK1RtVnZkbWx0UEM5MGFYUnNaVDQ4Y0dGMGFDQmtQU0pOTWk0eU1UUWdOQzQ1TlRSMk1UTXVOakUxVERjdU5qVTFJREkwVmpFd0xqTXhORXd6TGpNeE1pQXpMamcwTlNBeUxqSXhOQ0EwTGprMU5IcHROQzQ1T1RrZ01UY3VPVGhzTFRRdU5UVTNMVFF1TlRRNFZqVXVNVE0yYkM0MU9TMHVOVGsySURNdU9UWTNJRFV1T1RBNGRqRXlMalE0TlhwdE1UUXVOVGN6TFRRdU5EVTNiQzB1T0RZeUxqa3pOeTAwTGpJMExUWXVNemMyVmpCc05TNHdOamdnTlM0d09USXVNRE0wSURFekxqTTROWHBOTnk0ME16RXVNREF4YkRFeUxqazVPQ0F4T1M0NE16VXRNeTQyTXpjZ015NDJNemRNTXk0M09EY2dNeTQyT0RNZ055NDBNeUF3ZWlJdlBqd3ZjM1puUGc9PSIvPjx0ZXh0IGFyaWEtaGlkZGVuPSJ0cnVlIiB4PSI0MzUiIHk9IjE1MCIgZmlsbD0iIzAxMDEwMSIgZmlsbC1vcGFjaXR5PSIuMyIgdHJhbnNmb3JtPSJzY2FsZSguMSkiIHRleHRMZW5ndGg9IjQxMCI+TmVvdmltPC90ZXh0Pjx0ZXh0IHg9IjQzNSIgeT0iMTQwIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgZmlsbD0iI2ZmZiIgdGV4dExlbmd0aD0iNDEwIj5OZW92aW08L3RleHQ+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9IjgwNSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iMTcwIj4wLjU8L3RleHQ+PHRleHQgeD0iODA1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSIxNzAiPjAuNTwvdGV4dD48L2c+PC9zdmc+"> <a href="https://cj.rs/riss">

  <img loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0icG93ZXJlZCBieTogcmlzcyI+PHRpdGxlPnBvd2VyZWQgYnk6IHJpc3M8L3RpdGxlPjxsaW5lYXJHcmFkaWVudCBpZD0icyIgeDI9IjAiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNiYmIiIHN0b3Atb3BhY2l0eT0iLjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iLjEiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iciI+PHJlY3Qgd2lkdGg9IjEwNCIgaGVpZ2h0PSIyMCIgcng9IjMiIGZpbGw9IiNmZmYiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNyKSI+PHJlY3Qgd2lkdGg9Ijc1IiBoZWlnaHQ9IjIwIiBmaWxsPSIjNTU1Ii8+PHJlY3QgeD0iNzUiIHdpZHRoPSIyOSIgaGVpZ2h0PSIyMCIgZmlsbD0iIzlmOWY5ZiIvPjxyZWN0IHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIGZpbGw9InVybCgjcykiLz48L2c+PGcgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IlZlcmRhbmEsR2VuZXZhLERlamFWdSBTYW5zLHNhbnMtc2VyaWYiIHRleHQtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iIGZvbnQtc2l6ZT0iMTEwIj48dGV4dCBhcmlhLWhpZGRlbj0idHJ1ZSIgeD0iMzg1IiB5PSIxNTAiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgeD0iMzg1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9Ijg4NSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0Pjx0ZXh0IHg9Ijg4NSIgeT0iMTQwIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgZmlsbD0iI2ZmZiIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0PjwvZz48L3N2Zz4="></a> 
<img alt="GitHub tag (latest SemVer)" loading="lazy" src="https://img.shields.io/github/v/tag/cljoly/bepo.nvim?color=darkgreen&sort=semver"> 
<img alt="Endpoint Badge" loading="lazy" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fd.cj.rs%2Fnvim%2Fbepo.json&cacheSeconds=90000"></p>

</div>

<p>Mappings for the <a href="https://bepo.fr/">bepo</a> layout in Lua, inspired by <a href="https://github.com/michamos/vim-bepo">vim-bepo</a></p>
<p>It aims to be faster (takes only 0.4ms at startup on my machine, while <a href="https://github.com/michamos/vim-bepo">vim-bepo</a> requires 1.6ms) and more customizable (see <code>:help bepo.nvim-cherry-pick</code>)</p>
<h2 id="installation">Installation</h2>
<p>Add the plugin <code>cljoly/bepo.nvim</code> to your package manager.</p>
<p>If you don’t have a package manager yet, I use <a href="https://github.com/savq/paq-nvim">paq.nvim</a>.
You can give it a try :).<br>
Look at the <a href="https://github.com/savq/paq-nvim?tab=readme-ov-file#installation">installation instructions</a> and then add the following to your config:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">require</span> <span style="color:#98c379">&#34;paq&#34;</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#98c379">&#34;cljoly/bepo.nvim&#34;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#7f848e">-- ... other packages</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Then restart neovim and run <code>PaqInstall</code>. That will install the bepo.nvim plugin.</p>
<h2 id="usage">Usage</h2>
<p>Once the plugin is installed, you can add a call to set up the bepo mappings to your config:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;bepo&#34;</span>).<span style="color:#e06c75">setup</span>()
</span></span></code></pre></div><p>You should call the extension early in your <code>init.lua</code>, so that you can erase mappings later (or with other extensions) more easily.</p>
<h2 id="advanced-usage">Advanced Usage</h2>
<p>You can also apply only some mapping groups:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;bepo&#34;</span>).<span style="color:#e06c75">movement</span>()
</span></span><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;bepo&#34;</span>).<span style="color:#e06c75">tabs</span>()
</span></span><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;bepo&#34;</span>).<span style="color:#e06c75">easy_access</span>()
</span></span><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;bepo&#34;</span>).<span style="color:#e06c75">window</span>()
</span></span></code></pre></div><p>See <a href="https://github.com/cljoly/bepo.nvim/blob/main/doc/bepo-nvim.txt">the help file</a> for more details and the mappings installed.</p>
<h2 id="reporting-bugs">Reporting bugs</h2>
<p>Thanks for considering reporting a bug!</p>
<p>We want to know if the issue is caused by:</p>
<ol>
<li>the plugin itself</li>
<li>your user configuration</li>
<li>the system configuration</li>
</ol>
<p>Please try to reproduce the bug in nvim stripped of your user configuration by running:</p>
<pre tabindex="0"><code>nvim --clean -u test/mock_setup.lua
</code></pre><p>If you can’t reproduce the bug in that “clean” nvim instance, it suggests that the bug comes from an interaction of the plugin with your user configuration. Please feel free to open an issue, mentioning that.</p>
]]></content:encoded></item><item><title>Local NeoVim Plugin Development</title><link>https://cj.rs/blog/tips/nvim-plugin-development/</link><pubDate>Tue, 02 Nov 2021 14:28:13 +0000</pubDate><guid>https://cj.rs/blog/tips/nvim-plugin-development/</guid><description>&lt;div class="alert alert-note"&gt;
&lt;p class="alert-heading"&gt;
ℹ️
Note
&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2023-05-20&lt;/strong&gt;: Updated to account for the features of NeoVim 0.9 and obsolete plugins&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You have found a (Neo)Vim plugin that you want to fiddle with, either to contribute changes upstream or for your own use. Sounds familiar? Here are some tips and tricks I use for my &lt;a href="https://cj.rs/telescope-repo-nvim/"&gt;NeoVim plugin development&lt;/a&gt;. The aim of these small tricks is to iterate faster on changes, by loading your changes in a live NeoVim instance as quickly as possible.&lt;/p&gt;</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-note">
    <p class="alert-heading">
      ℹ️
      
        Note
      
    </p>
    <p><strong>2023-05-20</strong>: Updated to account for the features of NeoVim 0.9 and obsolete plugins</p>
  </div>



<p>You have found a (Neo)Vim plugin that you want to fiddle with, either to contribute changes upstream or for your own use. Sounds familiar? Here are some tips and tricks I use for my <a href="https://cj.rs/telescope-repo-nvim/">NeoVim plugin development</a>. The aim of these small tricks is to iterate faster on changes, by loading your changes in a live NeoVim instance as quickly as possible.</p>
<p>We will use <a href="https://cj.rs/telescope-repo-nvim/">telescope-repo.nvim</a> as an example but it is applicable to any plugin (although some sections of this post only apply to Lua NeoVim plugins).</p>
<h2 id="load-local-plugin-version">Load Local Plugin Version</h2>
<p>When developing, you make your changes to a local git repository, for instance <code>~/ghq/github.com/cljoly/telescope-repo.nvim</code>. To test those changes, you need to tell NeoVim to load the plugin from the local repository, instead of using the location set by your plugin manager. To do this, just append the following near the beginning of your <code>init.lua</code> file:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">vim.opt</span>.<span style="color:#e06c75">runtimepath</span>:<span style="color:#e06c75">prepend</span>(<span style="color:#98c379">&#34;~/ghq/github.com/cljoly/telescope-repo.nvim&#34;</span>)
</span></span></code></pre></div><p>Note the <code>:prepend</code>. It will ensure that your local dev version will override anything installed by your nvim package manager. This way no need to uninstall your plugin when developing!</p>
<h2 id="reloading-changes-in-a-live-neovim-instance">Reloading Changes in a Live NeoVim Instance</h2>
<p>You have now made some changes to the plugin that you would like to test. In doing so, you start a second NeoVim instance and open a set of files, change a bunch of settings…</p>
<p>You then change something in the code, but you don’t want to restart you test NeoVim instance, as that would mean reopening files and altering settings all over again. But just doing <code>require(…)</code> is not enough, because <code>require</code> caches already loaded files and doesn’t reload them if they are loaded already.</p>
<p>If you use <a href="https://github.com/nvim-telescope/telescope.nvim">telescope</a>, you can use the <code>reloader</code> picker. You can then select the module you want to reload, like so:</p>
<figure>
    <img loading="lazy" src="./telescope-reloader.png"
         alt="You type “telescop repo” and the corresponding Lua module surfaces. It will be reloaded when you hit Enter."/> <figcaption>
            Telescope reloader in action<p>You type “telescop repo” and the corresponding Lua module surfaces. It will be reloaded when you hit Enter.</p>
        </figcaption>
</figure>

<p>Under the hood, <code>telescope reload</code> <a href="https://github.com/nvim-telescope/telescope.nvim/blob/587a10d1494d8ffa1229246228f0655db2f0a48a/lua/telescope/builtin/internal.lua#L712">uses plenary</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;plenary.reload&#34;</span>).<span style="color:#e06c75">reload_module</span>(<span style="color:#e06c75">selection.value</span>)
</span></span></code></pre></div><p>which handle various cases (like whether <a href="https://github.com/lewis6991/impatient.nvim">impatient.nvim</a> is used at the time of writing). Despite this careful handling, sometimes, a plugin may not fully reload. In that case, you want to automate as much of the setup as possible on NeoVim restart.</p>
<h2 id="if-all-else-fails">If All Else Fails</h2>
<p>Sometimes, a “soft” reloading is not enough and you need to restart NeoVim. For instance, if we are testing the <code>:Telescope repo list</code> command and need to open <code>/tmp/file</code>, we can do:</p>
<pre tabindex="0"><code>nvim +&#39;Telescope repo list&#39; /tmp/file
</code></pre><p>and even place this in an infinite loop with a <code>sleep</code> command, to escape the loop more easily once we are done.</p>
<p>Of course, you will want to replace <code>Telescope repo list</code> with the command you want to test. You can also add more setup by supplying multiple “<code>+'…'</code>” arguments)</p>
<h2 id="all-set">All Set</h2>
<p>Now that you are all set, you can go on and write complete plugins! You may find the following resources useful in your journey:</p>
<ul>
<li>the <a href="https://neovim.io/doc/user/lua-guide.html"><code>:help lua-guide.txt</code></a> has a lot of resources to write plugins and alter NeoVim’s configuration,
<ul>
<li><a href="https://www.2n.pl/blog/how-to-write-neovim-plugins-in-lua">2n.pl</a> walks through writing a simple plugin,</li>
</ul>
</li>
<li><a href="https://web.archive.org/web/20211207190156/https://www.chrisatmachine.com/Neovim/28-neovim-lua-development/">set up a Language Server Protocol for Lua</a>,
<ul>
<li>and maybe even a full-blown <a href="https://github.com/folke/neodev.nvim">plugin</a> for Lua plugin development,</li>
</ul>
</li>
<li>to test a piece of Lua code real quick, <code>:=my.lua.code(here)</code> is great. For bigger pieces of code, <a href="https://github.com/rafcamlet/nvim-luapad">luapad</a> can be useful: it provides a scratch buffer where the result of a Lua snippet is displayed as you type.</li>
</ul>
]]></content:encoded></item><item><title>telescope-repo.nvim</title><link>https://cj.rs/telescope-repo-nvim/</link><pubDate>Sat, 21 Aug 2021 10:02:37 +0100</pubDate><guid>https://cj.rs/telescope-repo-nvim/</guid><description>🦘 Jump into the repositories of your filesystem, without any setup</description><content:encoded><![CDATA[
<p style="display: flex; justify-content: space-between">
  <a href="https://github.com/cljoly/telescope-repo.nvim" data-goatcounter-click="ext-github-telescope-repo.nvim" data-goatcounter-title="cljoly/telescope-repo.nvim">
    <span class="svgicon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
    stroke-linecap="round" stroke-linejoin="round">
    <path
        d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22">
    </path>
</svg></span>&nbsp;cljoly/telescope-repo.nvim
  </a>
  <a class="badges" href="https://github.com/cljoly/telescope-repo-nvim" data-goatcounter-click="ext-stargithub-telescope-repo.nvim" data-goatcounter-title="stars cljoly/telescope-repo.nvim">
    <img src="https://img.shields.io/github/stars/cljoly/telescope-repo.nvim?style=social" alt="Github stars for telescope-repo.nvim">
  </a>
</p>


<div class="badges">

<p>

  <img alt="Neovim version" loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDUiIGhlaWdodD0iMjAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0iTmVvdmltOiAwLjcrIj48dGl0bGU+TmVvdmltOiAwLjcrPC90aXRsZT48bGluZWFyR3JhZGllbnQgaWQ9InMiIHgyPSIwIiB5Mj0iMTAwJSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjYmJiIiBzdG9wLW9wYWNpdHk9Ii4xIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xIi8+PC9saW5lYXJHcmFkaWVudD48Y2xpcFBhdGggaWQ9InIiPjxyZWN0IHdpZHRoPSIxMDUiIGhlaWdodD0iMjAiIHJ4PSIzIiBmaWxsPSIjZmZmIi8+PC9jbGlwUGF0aD48ZyBjbGlwLXBhdGg9InVybCgjcikiPjxyZWN0IHdpZHRoPSI2OCIgaGVpZ2h0PSIyMCIgZmlsbD0iIzU1NSIvPjxyZWN0IHg9IjY4IiB3aWR0aD0iMzciIGhlaWdodD0iMjAiIGZpbGw9IiM1N2ExNDMiLz48cmVjdCB3aWR0aD0iMTA1IiBoZWlnaHQ9IjIwIiBmaWxsPSJ1cmwoI3MpIi8+PC9nPjxnIGZpbGw9IiNmZmYiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJWZXJkYW5hLEdlbmV2YSxEZWphVnUgU2FucyxzYW5zLXNlcmlmIiB0ZXh0LXJlbmRlcmluZz0iZ2VvbWV0cmljUHJlY2lzaW9uIiBmb250LXNpemU9IjExMCI+PGltYWdlIHg9IjUiIHk9IjMiIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgaHJlZj0iZGF0YTppbWFnZS9zdmcreG1sO2Jhc2U2NCxQSE4yWnlCbWFXeHNQU0lqTlRkQk1UUXpJaUJ5YjJ4bFBTSnBiV2NpSUhacFpYZENiM2c5SWpBZ01DQXlOQ0F5TkNJZ2VHMXNibk05SW1oMGRIQTZMeTkzZDNjdWR6TXViM0puTHpJd01EQXZjM1puSWo0OGRHbDBiR1UrVG1WdmRtbHRQQzkwYVhSc1pUNDhjR0YwYUNCa1BTSk5NaTR5TVRRZ05DNDVOVFIyTVRNdU5qRTFURGN1TmpVMUlESTBWakV3TGpNeE5Fd3pMak14TWlBekxqZzBOU0F5TGpJeE5DQTBMamsxTkhwdE5DNDVPVGtnTVRjdU9UaHNMVFF1TlRVM0xUUXVOVFE0VmpVdU1UTTJiQzQxT1MwdU5UazJJRE11T1RZM0lEVXVPVEE0ZGpFeUxqUTROWHB0TVRRdU5UY3pMVFF1TkRVM2JDMHVPRFl5TGprek55MDBMakkwTFRZdU16YzJWakJzTlM0d05qZ2dOUzR3T1RJdU1ETTBJREV6TGpNNE5YcE5OeTQwTXpFdU1EQXhiREV5TGprNU9DQXhPUzQ0TXpVdE15NDJNemNnTXk0Mk16ZE1NeTQzT0RjZ015NDJPRE1nTnk0ME15QXdlaUl2UGp3dmMzWm5QZz09Ii8+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9IjQzNSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iNDEwIj5OZW92aW08L3RleHQ+PHRleHQgeD0iNDM1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSI0MTAiPk5lb3ZpbTwvdGV4dD48dGV4dCBhcmlhLWhpZGRlbj0idHJ1ZSIgeD0iODU1IiB5PSIxNTAiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiB0ZXh0TGVuZ3RoPSIyNzAiPjAuNys8L3RleHQ+PHRleHQgeD0iODU1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSIyNzAiPjAuNys8L3RleHQ+PC9nPjwvc3ZnPg=="> <a href="https://cj.rs/riss">

  <img loading="lazy" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIHJvbGU9ImltZyIgYXJpYS1sYWJlbD0icG93ZXJlZCBieTogcmlzcyI+PHRpdGxlPnBvd2VyZWQgYnk6IHJpc3M8L3RpdGxlPjxsaW5lYXJHcmFkaWVudCBpZD0icyIgeDI9IjAiIHkyPSIxMDAlIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiNiYmIiIHN0b3Atb3BhY2l0eT0iLjEiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3Atb3BhY2l0eT0iLjEiLz48L2xpbmVhckdyYWRpZW50PjxjbGlwUGF0aCBpZD0iciI+PHJlY3Qgd2lkdGg9IjEwNCIgaGVpZ2h0PSIyMCIgcng9IjMiIGZpbGw9IiNmZmYiLz48L2NsaXBQYXRoPjxnIGNsaXAtcGF0aD0idXJsKCNyKSI+PHJlY3Qgd2lkdGg9Ijc1IiBoZWlnaHQ9IjIwIiBmaWxsPSIjNTU1Ii8+PHJlY3QgeD0iNzUiIHdpZHRoPSIyOSIgaGVpZ2h0PSIyMCIgZmlsbD0iIzlmOWY5ZiIvPjxyZWN0IHdpZHRoPSIxMDQiIGhlaWdodD0iMjAiIGZpbGw9InVybCgjcykiLz48L2c+PGcgZmlsbD0iI2ZmZiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9IlZlcmRhbmEsR2VuZXZhLERlamFWdSBTYW5zLHNhbnMtc2VyaWYiIHRleHQtcmVuZGVyaW5nPSJnZW9tZXRyaWNQcmVjaXNpb24iIGZvbnQtc2l6ZT0iMTEwIj48dGV4dCBhcmlhLWhpZGRlbj0idHJ1ZSIgeD0iMzg1IiB5PSIxNTAiIGZpbGw9IiMwMTAxMDEiIGZpbGwtb3BhY2l0eT0iLjMiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgeD0iMzg1IiB5PSIxNDAiIHRyYW5zZm9ybT0ic2NhbGUoLjEpIiBmaWxsPSIjZmZmIiB0ZXh0TGVuZ3RoPSI2NTAiPnBvd2VyZWQgYnk8L3RleHQ+PHRleHQgYXJpYS1oaWRkZW49InRydWUiIHg9Ijg4NSIgeT0iMTUwIiBmaWxsPSIjMDEwMTAxIiBmaWxsLW9wYWNpdHk9Ii4zIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0Pjx0ZXh0IHg9Ijg4NSIgeT0iMTQwIiB0cmFuc2Zvcm09InNjYWxlKC4xKSIgZmlsbD0iI2ZmZiIgdGV4dExlbmd0aD0iMTkwIj5yaXNzPC90ZXh0PjwvZz48L3N2Zz4="></a> 
<img alt="GitHub tag (latest SemVer)" loading="lazy" src="https://img.shields.io/github/v/tag/cljoly/telescope-repo.nvim?color=darkgreen&sort=semver"> 
<img alt="Endpoint Badge" loading="lazy" src="https://img.shields.io/endpoint?url=https%3A%2F%2Fd.cj.rs%2Fnvim%2Ftelescope-repo.json&cacheSeconds=90000"></p>

</div>

<p><code>telescope-repo</code> is an extension for <a href="https://github.com/nvim-telescope/telescope.nvim">telescope.nvim</a> that searches the filesystem for <em>git</em> (or other SCM<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, like <em>Pijul</em>, <em>Mercurial</em>…) repositories. It does not require any setup: the list of repositories is built on the fly over your whole <code>$HOME</code>, you don’t need to manually add projects or open some folders to populate this list, as opposed to <a href="https://github.com/nvim-telescope/telescope-project.nvim">telescope-project.nvim</a> or <a href="https://github.com/ahmedkhalf/project.nvim">project.nvim</a>.</p>
<p>Finding the repositories with “telescope” in their name, with the README in the panel on the top:</p>
<div id="demo3"></div>
<script>
AsciinemaPlayer.create("/telescope-repo-nvim/telescope.json", document.getElementById('demo3'), {
"autoPlay":  true ,"loop":  true ,"preload":  1 ,"start-at": "1",
});
</script>
<noscript><blockquote><p>To run this asciicast without javascript, use <code>asciinema play https://cj.rs/telescope-repo-nvim/telescope.json</code> with <a href="https://asciinema.org/">Asciinema</a></p></blockquote></noscript>

<p>Use cases include:</p>
<ul>
<li>If you don’t start vim from the shell (from a GUI or as the start command of a terminal), you are most likely in your <code>$HOME</code> directory. You then want to jump into your code as quickly as possible and this plugin can help!</li>
<li>Sometimes, you have the definition of a function and use of it in different repositories  (e.g. a library you wrote and a program using this library). This plugin helps to open the two, for instance in two splits.</li>
<li>Use of less popular SCMs: some similar extensions rely on strong conventions to find repositories, like “directories containing a <code>.git</code> file that is also a directory, all inside directory <code>X</code>”. Less popular SCMs like <a href="https://pijul.org/">Pijul</a> have a different folder name, and even <a href="https://git-scm.com/docs/git-worktree"><code>git worktree</code></a>s don’t fit neatly into these constraint, with their <code>.git</code> <em>files</em>.</li>
</ul>
<p><code>telescope-repo.nvim</code> is based on <a href="https://github.com/nvim-telescope/telescope-ghq.nvim">telescope-ghq.nvim</a></p>
<h2 id="installation">Installation</h2>
<p>You need to add these in your plugin management system<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#98c379">&#39;nvim-lua/plenary.nvim&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#98c379">&#39;nvim-telescope/telescope.nvim&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#98c379">&#39;cljoly/telescope-repo.nvim&#39;</span>
</span></span></code></pre></div><p>And optionally, to load the extension:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">require</span><span style="color:#98c379">&#39;telescope&#39;</span>.<span style="color:#e06c75">load_extension</span><span style="color:#98c379">&#39;repo&#39;</span>
</span></span></code></pre></div><p>A handy companion plugin is <a href="https://github.com/airblade/vim-rooter">vim-rooter</a>, as it’ll change the current directory according to the current file’s detected project (often, the root of the git repository). To get it to change each <em>buffer’s</em> directory, instead of the whole editor by default, add the following Lua to your configuration:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">g</span>[<span style="color:#98c379">&#39;rooter_cd_cmd&#39;</span>] <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#39;lcd&#39;</span>
</span></span></code></pre></div><h3 id="packer">Packer</h3>
<p>For instance, with <a href="https://github.com/wbthomason/packer.nvim">Packer.nvim</a>:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">use</span> <span style="color:#98c379">&#39;cljoly/telescope-repo.nvim&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#e06c75">use</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#98c379">&#39;nvim-telescope/telescope.nvim&#39;</span>,
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">requires</span> <span style="color:#56b6c2">=</span> { {<span style="color:#98c379">&#39;nvim-lua/plenary.nvim&#39;</span>} }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="external-dependencies">External Dependencies</h2>
<h3 id="required">Required</h3>
<ul>
<li><a href="https://github.com/sharkdp/fd"><code>fd</code></a> to find the repositories on the filesystem with <code>list</code></li>
<li><a href="https://man.archlinux.org/man/plocate.1"><code>plocate</code></a>, <a href="https://github.com/ngirard/lolcate-rs"><code>lolcate-rs</code></a> or <a href="https://man.archlinux.org/man/locate.1"><code>locate</code></a> to find the repositories on the filesystem with <code>cached_list</code></li>
</ul>
<h3 id="optional">Optional</h3>
<ul>
<li><a href="https://github.com/charmbracelet/glow"><code>glow</code></a> to preview markdown files, will fall back to <a href="https://github.com/sharkdp/bat"><code>bat</code></a> if not present (and uses <code>cat</code> if neither are present)</li>
</ul>
<h2 id="usage">Usage</h2>
<h3 id="global-configuration">Global Configuration</h3>
<p>You can change the default argument given to subcommands (like <a href="#list"><code>list</code></a> or <a href="#cached_list"><code>cached_list</code></a>) using the telescope <code>setup</code> function with a table like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">extensions</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">repo</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#56b6c2">&lt;</span><span style="color:#e06c75">subcommand</span><span style="color:#56b6c2">&gt;</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#56b6c2">&lt;</span><span style="color:#e06c75">argument</span><span style="color:#56b6c2">&gt;</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>          <span style="color:#98c379">&#34;new&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#98c379">&#34;default&#34;</span>,
</span></span><span style="display:flex;"><span>          <span style="color:#98c379">&#34;value&#34;</span>,
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>      <span style="color:#e06c75">settings</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#e06c75">auto_lcd</span> <span style="color:#56b6c2">=</span> <span style="color:#e5c07b">true</span>,
</span></span><span style="display:flex;"><span>      }
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>for instance, you could do:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;telescope&#34;</span>).<span style="color:#e06c75">setup</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">extensions</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#e06c75">repo</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#e06c75">list</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#e06c75">fd_opts</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>          <span style="color:#98c379">&#34;--no-ignore-vcs&#34;</span>,
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#e06c75">search_dirs</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>          <span style="color:#98c379">&#34;~/my_projects&#34;</span>,
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>      },
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;telescope&#34;</span>).<span style="color:#e06c75">load_extension</span> <span style="color:#98c379">&#34;repo&#34;</span>
</span></span></code></pre></div><p><strong>Note</strong>: make sure to have <code>require(&quot;telescope&quot;).load_extension &quot;repo&quot;</code> <em>after</em> the call to <code>require(&quot;telescope&quot;).setup {…}</code>, otherwise the global configuration won’t be taken into account.</p>
<h3 id="list"><code>list</code></h3>
<p><code>:Telescope repo list</code> or <code>lua require'telescope'.extensions.repo.list{}</code></p>
<p>Running <code>repo list</code> and list repositories&rsquo; paths.</p>
<table>
  <thead>
      <tr>
          <th>key</th>
          <th>action</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>&lt;CR&gt;</code> (edit)</td>
          <td><code>builtin.git_files</code> for git, falls back to <code>builtin.find_files</code> for other SCMs</td>
      </tr>
      <tr>
          <td><code>&lt;C-v&gt;</code> (vertical)</td>
          <td><code>builtin.live_grep</code> in the selected project</td>
      </tr>
      <tr>
          <td><code>&lt;C-t&gt;</code> (tab)</td>
          <td>Same as <code>&lt;CR&gt;</code> but opens a new tab. Also, does a <code>cd</code> into the project’s directory for this tab only</td>
      </tr>
  </tbody>
</table>
<h4 id="options">Options</h4>
<h5 id="bin"><code>bin</code></h5>
<p>Filepath for the binary <code>fd</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-vim" data-lang="vim"><span style="display:flex;"><span><span style="color:#7f848e">&#34; path can be expanded</span>
</span></span><span style="display:flex;"><span>:<span style="color:#e06c75">Telescope</span> <span style="color:#e06c75">repo</span> <span style="color:#e06c75">list</span> <span style="color:#e06c75">bin</span>=~/<span style="color:#e06c75">fd</span>
</span></span></code></pre></div><h5 id="pattern"><code>pattern</code></h5>
<p>Pattern of the SCM database folder.</p>
<p>Default value: <code>[[^\.git$]]</code></p>
<h5 id="cwd"><code>cwd</code></h5>
<p>Transform the result paths into relative ones with this value as the base dir.</p>
<p>Default value: <code>vim.fn.getcwd()</code></p>
<h5 id="fd_opts"><code>fd_opts</code></h5>
<p><strong>This is a relatively advanced option that you should use with caution. There is no guarantee that a particular set of options would work the same across multiple versions</strong></p>
<p>This passes additional options to the command <code>fd</code> that generates the repository list. It is inserted like so:</p>
<pre tabindex="0"><code>fd [set of default options] [fd_opts] --exec [some default command] [pattern] …
</code></pre><h5 id="example">Example</h5>
<p>Let’s say you have a git repository <code>S</code> inside another git repository <code>M</code> (for instance because of <a href="https://github.com/cljoly/telescope-repo.nvim/issues/5">#5</a>), but <code>S</code> is in a directory that’s ignored in the <code>.gitignore</code> in <code>M</code>. <code>S</code> wouldn’t appear in the Telescope list of this extension by default, because it is ignored (<code>.gitignore</code> are taken into account by default).</p>
<p>To avoid taking into account the <code>.gitignore</code>, we need to pass <code>--no-ignore-vcs</code> to <code>fd</code>, like so (in NeoVim):</p>
<pre tabindex="0"><code>:lua require&#39;telescope&#39;.extensions.repo.list{fd_opts={&#39;--no-ignore-vcs&#39;}}
</code></pre><p>This will list <code>M</code> and <code>S</code> in the Telescope output! The downside is that listing repositories will be a little longer, as we don’t skip the git-ignored files anymore.</p>
<h5 id="search_dirs"><code>search_dirs</code></h5>
<p>This limits the search to a particular directory or set of directories.</p>
<h5 id="example-1">Example</h5>
<pre tabindex="0"><code>:lua require&#39;telescope&#39;.extensions.repo.list{search_dirs = {&#34;~/ghq/github.com&#34;, &#34;~/ghq/git.sr.ht&#34;}}
:lua require&#39;telescope&#39;.extensions.repo.list{search_dirs = {&#34;~/.local/share/nvim/site/pack&#34;}}
</code></pre><h5 id="tail_path"><code>tail_path</code></h5>
<p>Show only basename of the path.</p>
<p>Default value: <code>false</code></p>
<h5 id="shorten_path"><code>shorten_path</code></h5>
<p>Call <code>pathshorten()</code> for each path. This will for instance transform <code>/home/me/code/project</code> to <code>/h/m/c/project</code>.</p>
<p>Default value: <code>false</code></p>
<h4 id="examples">Examples</h4>
<p>Here is how you can use this plugin with various SCM:</p>
<table>
  <thead>
      <tr>
          <th>SCM</th>
          <th>Command</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>git</td>
          <td><code>:Telescope repo list</code> or <code>lua require'telescope'.extensions.repo.list{}</code></td>
      </tr>
      <tr>
          <td>pijul</td>
          <td><code>lua require'telescope'.extensions.repo.list{pattern=[[^\.pijul$]]}</code></td>
      </tr>
      <tr>
          <td>hg</td>
          <td><code>lua require'telescope'.extensions.repo.list{pattern=[[^\.hg$]]}</code></td>
      </tr>
      <tr>
          <td>fossil</td>
          <td><code>lua require'telescope'.extensions.repo.list{pattern=[[^\.fslckout$]]}</code></td>
      </tr>
  </tbody>
</table>
<p>Is your favorite SCM missing? It should be straightforward to support it by changing the pattern parameter. If you want it to be considered for addition here, open a PR!</p>
<h3 id="cached_list"><code>cached_list</code></h3>
<p><code>:Telescope repo cached_list</code></p>
<p>This relies on a <code>locate</code> command to find repositories. This should be much faster than the <code>list</code> command, as it relies on a pre-built index but results may be stalled.</p>
<h4 id="general-notes-for-updatedb">General Notes for <code>updatedb</code></h4>
<p>At this point, the plugin does not manage index update.</p>
<p>Updating the index often requires to run a command like <code>updatedb</code> as root. If the cached_list command does not return anything, it might be due to the configuration of the <code>updatedb</code> command. You can run <code>sudo updatedb -v --debug-pruning</code> to see if the directories you expect are indexed. You should see lines like the following if a repository named &ldquo;rusqlie_migration&rdquo; is indexed:</p>
<pre tabindex="0"><code>/home/cjoly/worktree/rusqlite/rusqlite_migration/Cargo.toml
/home/cjoly/worktree/rusqlite/rusqlite_migration/README.md
/home/cjoly/worktree/rusqlite/rusqlite_migration/.git
</code></pre><p>You might need to tweak the settings in the file <code>/etc/updatedb.conf</code>. The <a href="https://man.archlinux.org/man/updatedb.conf.5">corresponding manpage</a> may be useful as well.</p>
<h4 id="notes-for-macos">Notes for MacOS</h4>
<p><code>glocate</code> command used for caching on MacOS comes with gnu <code>findutils</code> which can be installed with</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>brew install findutils
</span></span></code></pre></div><p>With <code>glocate</code> installed use, add the following to your <code>.bashrc</code>/<code>.zshrc</code></p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#7f848e"># https://egeek.me/2020/04/18/enabling-locate-on-osx/</span>
</span></span><span style="display:flex;"><span><span style="color:#c678dd">if</span> which glocate &gt; /dev/null; <span style="color:#c678dd">then</span>
</span></span><span style="display:flex;"><span>  <span style="color:#e5c07b">alias</span> <span style="color:#e06c75">locate</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;glocate -d </span><span style="color:#e06c75">$HOME</span><span style="color:#98c379">/locatedb&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#7f848e"># Using cache_list requires `LOCATE_PATH` environment var to exist in session.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#7f848e"># trouble shoot: `echo $LOCATE_PATH` needs to return db path.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#56b6c2">[[</span> -f <span style="color:#98c379">&#34;</span><span style="color:#e06c75">$HOME</span><span style="color:#98c379">/locatedb&#34;</span> <span style="color:#56b6c2">]]</span> <span style="color:#56b6c2">&amp;&amp;</span> <span style="color:#e5c07b">export</span> <span style="color:#e06c75">LOCATE_PATH</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;</span><span style="color:#e06c75">$HOME</span><span style="color:#98c379">/locatedb&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#c678dd">fi</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#e5c07b">alias</span> <span style="color:#e06c75">loaddb</span><span style="color:#56b6c2">=</span><span style="color:#98c379">&#34;gupdatedb --localpaths=</span><span style="color:#e06c75">$HOME</span><span style="color:#98c379"> --prunepaths=/Volumes --output=</span><span style="color:#e06c75">$HOME</span><span style="color:#98c379">/locatedb&#34;</span>
</span></span></code></pre></div><p>After you have run <code>loaddb</code> the first time you need to reload the shell to make sure that it
exports the <code>LOCATE_PATH</code> variable. Then the following command should work:</p>
<pre tabindex="0"><code>lua require&#39;telescope&#39;.extensions.repo.cached_list()
</code></pre><p>If nothing is shown, even after a little while, try this:</p>
<pre tabindex="0"><code>lua require&#39;telescope&#39;.extensions.repo.cached_list{locate_opts={&#34;-d&#34;, vim.env.HOME .. &#34;/locatedb&#34;}}
</code></pre>



  
  
  
  

  <div class="alert alert-warning">
    <p class="alert-heading">
      ⚠️
      
        Warning
      
    </p>
    <p>Installation and use of the plugin on systems other than GNU/Linux is community-maintained. Don&rsquo;t hesitate to open <a href="https://github.com/cljoly/telescope-repo.nvim/discussions/categories/q-a">a discussion</a> or <a href="https://github.com/cljoly/telescope-repo.nvim/pulls">a pull-request</a> if something is not working!</p>
  </div>



<h4 id="troubleshooting">Troubleshooting</h4>
<p>You should try to run:</p>
<pre tabindex="0"><code>sudo updatedb
</code></pre><p>if you encounter any problems. If it’s not the case by default, you should automate such index update with for instance <code>cron</code> or <code>systemd-timers</code>. See <a href="https://wiki.archlinux.org/title/Locate">https://wiki.archlinux.org/title/Locate</a> and <a href="https://github.com/cljoly/telescope-repo.nvim/discussions/64">this discussion</a> for various ways to automate this.</p>
<h4 id="options-1">Options</h4>
<p>Options are the similar to <code>repo list</code>, bearing in mind that we use <code>locate</code> instead of <code>fd</code>. Note that the following <code>list</code> options are not supported in <code>cached_list</code>:</p>
<ul>
<li><code>fd_opts</code>, as we don’t use <code>fd</code> with <code>cached_list</code>,</li>
<li><code>search_dirs</code>, as <code>locate</code> does not accept a directory to search in.</li>
</ul>
<h4 id="examples-1">Examples</h4>
<h5 id="exclude-irrelevant-results">Exclude Irrelevant Results</h5>
<p>Chances are you will get results from folders you don’t care about like <code>.cache</code> or <code>.cargo</code>. In that case, you can use the <code>file_ignore_patterns</code> option of Telescope, like so (these are <a href="https://www.lua.org/manual/5.1/manual.html#5.4.1">Lua regexes</a>).</p>
<p>Hide all git repositories that may be in <code>.cache</code> or <code>.cargo</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span>:<span style="color:#e06c75">lua</span> <span style="color:#e06c75">require</span><span style="color:#98c379">&#39;telescope&#39;</span>.<span style="color:#e06c75">extensions.repo</span>.<span style="color:#e06c75">cached_list</span>{<span style="color:#e06c75">file_ignore_patterns</span><span style="color:#56b6c2">=</span>{<span style="color:#98c379">&#34;/%.cache/&#34;</span>, <span style="color:#98c379">&#34;/%.cargo/&#34;</span>}}
</span></span></code></pre></div><h6 id="notes">Notes</h6>
<ul>
<li>These patterns are used to filter the output of the <code>locate</code> command, so they don’t speed up the search in any way. You should use them mainly to exclude git repositories you won’t want to jump into, not in the hope to enhance performance.</li>
<li>The <code>%.</code> in Lua regex is an escaped <code>.</code> as <code>.</code> matches any characters.</li>
<li>These patterns are applied against whole paths like <code>/home/myuser/.cache/some/dir</code>, so if you want to exclude only <code>/home/myuser/.cache</code>, you need a more complicated pattern like so:</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span>:<span style="color:#e06c75">lua</span> <span style="color:#e06c75">require</span><span style="color:#98c379">&#39;telescope&#39;</span>.<span style="color:#e06c75">extensions.repo</span>.<span style="color:#e06c75">cached_list</span>{<span style="color:#e06c75">file_ignore_patterns</span><span style="color:#56b6c2">=</span>{<span style="color:#98c379">&#34;^&#34;</span><span style="color:#56b6c2">..</span> <span style="color:#e06c75">vim.env</span>.<span style="color:#e06c75">HOME</span> <span style="color:#56b6c2">..</span> <span style="color:#98c379">&#34;/%.cache/&#34;</span>, <span style="color:#98c379">&#34;^&#34;</span><span style="color:#56b6c2">..</span> <span style="color:#e06c75">vim.env</span>.<span style="color:#e06c75">HOME</span> <span style="color:#56b6c2">..</span> <span style="color:#98c379">&#34;/%.cargo/&#34;</span>}}
</span></span></code></pre></div><h5 id="use-with-other-scms">Use With Other SCMs</h5>
<p>Here is how you can use this plugin with various SCM (we match on the whole path with <code>locate</code>, so patterns differ slightly from <code>repo list</code>: notice the <code>^</code> that becomes a <code>/</code>):</p>
<table>
  <thead>
      <tr>
          <th>SCM</th>
          <th>Command</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>git</td>
          <td><code>:Telescope repo list</code> or <code>lua require'telescope'.extensions.repo.list{}</code></td>
      </tr>
      <tr>
          <td>pijul</td>
          <td><code>lua require'telescope'.extensions.repo.list{pattern=[[/\.pijul$]]}</code></td>
      </tr>
      <tr>
          <td>hg</td>
          <td><code>lua require'telescope'.extensions.repo.list{pattern=[[/\.hg$]]}</code></td>
      </tr>
      <tr>
          <td>fossil</td>
          <td><code>lua require'telescope'.extensions.repo.list{pattern=[[/\.fslckout$]]}</code></td>
      </tr>
  </tbody>
</table>
<h2 id="faq">FAQ</h2>
<h3 id="no-repositories-are-found">No repositories are found</h3>
<p>Make sure that <code>:checkhealth telescope</code> shows something like:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-markdown" data-lang="markdown"><span style="display:flex;"><span>## Telescope Extension: `repo`
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">-</span> OK: Will use <span style="color:#98c379">`glow`</span> to preview markdown READMEs
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">-</span> OK: Will use <span style="color:#98c379">`bat`</span> to preview non-markdown READMEs
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">-</span> OK: locate: found <span style="color:#98c379">`plocate`</span>
</span></span><span style="display:flex;"><span>    plocate 1.1.13
</span></span><span style="display:flex;"><span>    Copyright 2020 Steinar H. Gunderson
</span></span><span style="display:flex;"><span>    License GPLv2+: GNU GPL version 2 or later &lt;https://gnu.org/licenses/gpl.html&gt;.
</span></span><span style="display:flex;"><span>    This is free software: you are free to change and redistribute it.
</span></span><span style="display:flex;"><span>    There is NO WARRANTY, to the extent permitted by law.
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">-</span> INFO: Repos found for <span style="color:#98c379">`:Telescope repo cached_list`</span>:
</span></span><span style="display:flex;"><span>    /home/cj/.cache/yay/android-sdk/.git, /home/cj/.cache/yay/android-sdk-platform-tools/.git...
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">-</span> OK: fd: found <span style="color:#98c379">`fd`</span>
</span></span><span style="display:flex;"><span>    fd 8.3.0
</span></span><span style="display:flex;"><span>  <span style="color:#c678dd">-</span> INFO: Repos found for <span style="color:#98c379">`:Telescope repo list`</span>:
</span></span><span style="display:flex;"><span>    /home/cj/tmp/git_rst, /home/cj/qmk_firmware...
</span></span></code></pre></div><p><strong>This may take a few seconds to run</strong></p>
<p>The output of this command may point to missing dependencies.</p>
<h3 id="getting-the-repository-list-is-slow">Getting the repository list is slow</h3>
<p>If <code>:Telescope repo list</code> is slow, you can use your <code>.fdignore</code> to exclude some folders from your filesystem. You can even use a custom ignore file with the <code>--ignore-file</code> option, like so:</p>
<pre tabindex="0"><code>lua require&#39;telescope&#39;.extensions.repo.list{fd_opts=[[--ignore-file=myignorefile]]}
</code></pre><h2 id="contribute">Contribute</h2>
<p>Contributions are welcome, see this <a href="https://cj.rs/docs/contribute/">document</a>!</p>
<p>The telescope <a href="https://github.com/nvim-telescope/telescope.nvim/blob/master/developers.md">developers documentation</a> is very useful to understand how plugins work and you may find <a href="https://cj.rs/blog/tips/nvim-plugin-development/">these tips</a> useful as well.</p>
<h3 id="running-tests">Running tests</h3>
<p><a href="https://github.com/nvim-lua/plenary.nvim">Plenary.nvim</a> integration tests are executed as a part of the CI checks. However, they are very basic for now and you might be better off just testing the two commands locally.</p>
<h3 id="code-formatting--linting">Code Formatting &amp; Linting</h3>
<p><a href="https://github.com/johnnymorganz/stylua">StyLua</a> is used for code formatting. It is run like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>make fmt
</span></span></code></pre></div><p>To run the linter:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>make lint
</span></span></code></pre></div><h2 id="acknowledgement">Acknowledgement</h2>
<p>I would like to thank all the contributors to this plugin, as well as the developers of neovim and Telescope. Without them, none of this would be possible.</p>
<p>Thanks to <em>Code Smell</em> for demoing the plugin in <a href="https://youtu.be/indguFY7wJ0?t=267">5 Terrific Neovim Telescope Extensions for 2022 🔭</a>.</p>
<p>Furthermore, thanks to <a href="https://www.migadu.com/">Migadu</a> for offering a discounted service to support this project. It is not an endorsement by Migadu though.</p>
<h2 id="stability">Stability</h2>
<p>We understand that you need a reliable plugin that never breaks. To this end, code changes are first tested on our machines in a separate <code>dev</code> branch and once we are reasonably confident that changes don’t have unintended side-effects, they get merged to the <code>master</code> branch, where a wider user-base will get the changes. We also often tag releases, holding a more mature, coherent set of changes. If you are especially sensitive to changes, instruct your package manager to install the version pointed by a particular tag and watch for new releases <a href="https://github.blog/changelog/2018-11-27-watch-releases/">on GitHub</a> or <a href="https://ronaldsvilcins.com/2020/03/26/rss-feeds-for-your-github-releases-tags-and-activity/">via RSS</a>. Conversely, if you wish to live on the bleeding-edge, instruct your package manager to use the <code>dev</code> branch.</p>
<h2 id="changelog">Changelog</h2>
<h3 id="030">0.3.0</h3>
<ul>
<li>Add support for <code>lolcate-rs</code> as a <code>cached_list</code> provider</li>
<li>Add an option to restrict the search to some directories</li>
<li>Add fallback command so that <code>:Telescope repo</code> does not error</li>
<li>Fixes:
<ul>
<li>keep Telescope prompt in insert mode (nvim 0.7+)</li>
<li>the <code>search_dirs</code> argument is not mandatory</li>
</ul>
</li>
<li>Dev: add tests, CI, formatting with stylua</li>
<li>Documentation: update with new features, installation instructions, code formatting reference and other fixes</li>
</ul>
<h3 id="020">0.2.0</h3>
<ul>
<li>Add support for <code>checkhealth</code></li>
<li>Add picker that builds the list of repositories from <code>locate</code>, thus taking profit of a system-wide index.</li>
<li>Add mappings leading to various actions</li>
<li>Preview non-markdown Readme file</li>
</ul>
<h3 id="010">0.1.0</h3>
<ul>
<li>Basic feature, generate a dynamic project list with <code>fd</code></li>
<li>Falls back to file listing if we are not in a <code>git</code> repository</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>SCM: Source-Control Management&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>See also <a href="#stability">Stability</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item><item><title>How I Got Started with NeoVim’s Lua Configuration</title><link>https://cj.rs/blog/my-setup/nvim-0-5/</link><pubDate>Sun, 18 Jul 2021 22:57:01 +0100</pubDate><guid>https://cj.rs/blog/my-setup/nvim-0-5/</guid><description>&lt;p&gt;Four months ago I was still using &lt;a href="https://spacevim.org/"&gt;SpaceVim&lt;/a&gt; when I stumbled upon a &lt;a href="https://oroques.dev/notes/neovim-init/"&gt;blog post&lt;/a&gt; on how to configure NeoVim with Lua. I then started to create my own configuration. In this post I’ll share the learnings acquired in the process. I hope you will find this useful to create your own configuration!&lt;/p&gt;
&lt;h2 id="why-create-your-own-vim-config-from-scratch"&gt;Why create your own Vim config from scratch?&lt;/h2&gt;
&lt;p&gt;Vim has been my daily driver for about ten years. Almost from the beginning, I used Vim distributions for ease of configuration. &lt;a href="https://vim.spf13.com"&gt;Spf13 vim&lt;/a&gt; first and then &lt;a href="https://spacevim.org/"&gt;SpaceVim&lt;/a&gt;. With a distribution, one gets a lot of bells and whistle without spending too much time configuring things. So why spend hours setting up NeoVim from scratch?&lt;/p&gt;</description><content:encoded><![CDATA[<p>Four months ago I was still using <a href="https://spacevim.org/">SpaceVim</a> when I stumbled upon a <a href="https://oroques.dev/notes/neovim-init/">blog post</a> on how to configure NeoVim with Lua. I then started to create my own configuration. In this post I’ll share the learnings acquired in the process. I hope you will find this useful to create your own configuration!</p>
<h2 id="why-create-your-own-vim-config-from-scratch">Why create your own Vim config from scratch?</h2>
<p>Vim has been my daily driver for about ten years. Almost from the beginning, I used Vim distributions for ease of configuration. <a href="https://vim.spf13.com">Spf13 vim</a> first and then <a href="https://spacevim.org/">SpaceVim</a>. With a distribution, one gets a lot of bells and whistle without spending too much time configuring things. So why spend hours setting up NeoVim from scratch?</p>
<p>SpaceVim does provide a lot of options and plugins so that everyone can find the functionalities they need and discover some new useful tricks. This ends up creating long key mappings like <code>&lt;space&gt;ff</code> and leads to long startup time. For instance on my machine with an SSD drive, it needs between 1 and 2 seconds to start. This is a common problem with distributions.</p>
<p>The long startup time issue can be mitigated by starting Vim less often or by choosing a lighter Vim distribution. One could even reuse only the core and some parts of a distribution, leaving out the rest. Regarding the long key mappings issue, shorter ones could be redefined. However, this has two major drawbacks:</p>
<ul>
<li>your customized configuration lies on the moving foundations of the underlying distributions,</li>
<li>debugging is made more difficult because of the incorporation of so much code you haven’t written and don’t know well.</li>
</ul>
<p>To solve all these problems, I found it easier and less time-consuming to have my own configuration. Additional benefits include a better recall of what exists and what functionalities are implemented<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. The good news with NeoVim 0.5 is that you can now use Lua and not an arcane language like VimScript<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. The result is also quite fast: with my configuration NeoVim starts in 100 to 110 <em>milliseconds</em>.</p>
<p>Starting your own configuration does not mean you can’t draw inspiration from other configurations! The next sections of this post reference guides, snippets and plugins I’ve found useful, so that you can evaluate them for inclusion into your own file.</p>
<h2 id="enjoy-the-ride">Enjoy the Ride!</h2>
<p>To tailor my experience to my liking, I tried my changes in a new instance of NeoVim after every addition of a plugin or of a set of mappings. Mappings in particular sometime looks good on the screen, but fingers don’t like it that much 😀.</p>
<p>In addition, NeoVim configuration can be notoriously complicated to debug. To quote the documentation:</p>




  <figure>
    <blockquote cite="https://neovim.io/doc/user/starting.html#bisect">
      <p>To find the cause of a problem in your config, you must &ldquo;bisect&rdquo; it.</p>

    </blockquote>
    
      <figcaption class="blockquote-caption">
        
          <cite style="text-align: right"><a href="https://neovim.io/doc/user/starting.html#bisect">https://neovim.io/doc/user/starting.html#bisect</a></cite>
          <br/>
        
        
      </figcaption>
    
  </figure>



<p>For this the previous point about regular testing helps, but I would advise for versioning your configuration, for instance with <a href="/blog/my-setup/chezmoi/">git and chezmoi</a>. With regular commits, you will be able to rely on the git bisect feature when things go wrong.</p>
<h2 id="guides">Guides</h2>
<p>Detailed guidance exists online to get started with your configuration. I got started with <a href="https://oroques.dev/notes/neovim-init/">a mostly one file Lua configuration</a>. This guide contains general explainations on where to put your Lua files, the reasoning behind the changes… If you are not familiar with NeoVim Lua configuration tricks, I strongly recommend reading the above link. See you in a bit 👋.</p>
<p>In addition, these links may prove useful:</p>
<ul>
<li><a href="https://github.com/mjlbach/defaults.nvim/blob/master/init.lua">Example <code>init.lua</code></a></li>
<li><a href="https://emilienl.medium.com/a-quick-guide-to-set-up-nvim-built-in-lsp-419bb6e91c0a">A quick guide to set up nvim built in LSP</a></li>
<li><a href="https://neovim.io/doc/lua-resources/">NeoVim’s Lua Resources</a></li>
</ul>
<h2 id="snippets-from-my-configuration">Snippets from my Configuration</h2>
<p>This section assumes basic knowledge of Lua configuration.</p>
<h3 id="mapping-hints">Mapping Hints</h3>
<figure>
    <img loading="lazy" src="../which-key-space.png"/> <figcaption>
            Pop up window showing the keys that can follow &lt;space&gt;
        </figcaption>
</figure>

<p><a href="https://github.com/folke/which-key.nvim">Which-Key</a> is a brilliant plugin to display a popup with the possible mappings as you press the keys of the shortcut. For instance, I have mapped <code>&lt;space&gt;j</code> to <code>:tabnew</code>, to open a new tab. On the figure above, I’ve just typed <code>&lt;space&gt;</code> (abbreviated <code>SPC</code>) and the keys that could be pressed then are displayed in a popup at the bottom of the screen.
What sets <code>which-key.nvim</code> apart is that you don’t have to configuration your mappings using a special function for it to work. It discovers most of your mappings right away and use them to populate the popup.</p>
<p>I’ve tweaked <code>timeoutlen</code> to get the popup more quickly. I’ve also changed labels for some keys to have shorter or more explicit names, like <code>SPC</code> for <code>&lt;space&gt;</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#c678dd">local</span> <span style="color:#e06c75">wk</span> <span style="color:#56b6c2">=</span> <span style="color:#e06c75">require</span>(<span style="color:#98c379">&#34;which-key&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">wk.setup</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">key_labels</span> <span style="color:#56b6c2">=</span> {
</span></span><span style="display:flex;"><span>    [<span style="color:#98c379">&#34;&lt;space&gt;&#34;</span>] <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;SPC&#34;</span>,
</span></span><span style="display:flex;"><span>    [<span style="color:#98c379">&#34;&lt;CR&gt;&#34;</span>] <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;RET&#34;</span>,
</span></span><span style="display:flex;"><span>    [<span style="color:#98c379">&#34;&lt;tab&gt;&#34;</span>] <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;TAB&#34;</span>,
</span></span><span style="display:flex;"><span>  },
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#e06c75">vim.opt</span>.<span style="color:#e06c75">timeoutlen</span> <span style="color:#56b6c2">=</span> <span style="color:#d19a66">900</span>
</span></span></code></pre></div><h4 id="mapping-choices">Mapping Choices</h4>
<p>My custom mappings are mainly around the <code>&lt;space&gt;</code> key, often with only one key following, like <code>&lt;space&gt;s</code> to write the current file. The <code>&lt;Leader&gt;</code> key is mostly left for plugins, while the <code>&lt;LocalLeader&gt;</code> is sometimes used for some buffer local mappings.</p>
<p>In general, my mappings are heavily adapted to the <a href="https://bepo.fr">BÉPO layout</a>, a Dvorak-like keyboard for French.</p>
<h3 id="clever-f">Clever-F</h3>
<p><a href="https://github.com/rhysd/clever-f.vim">rhysd/clever-f.vim</a> extends <code>f</code>, <code>t</code>… so that:</p>
<ol>
<li>When you press <code>fc</code>, all characters <code>c</code> in the current line get highlighted</li>
<li>Another press on <code>f</code> jumps to the next occurence of <code>c</code></li>
<li>A press on <code>F</code> jumps backward</li>
</ol>
<p>This makes <code>,</code> and <code>;</code> redondant and these can be remapped.</p>
<h4 id="leaders">Leaders</h4>
<p>The universal leader is by default mapped on <code>\</code>. However, it is frequently remapped to <code>,</code>, which is easier to reach. Since clever-f freed two mappings, I reuse them like so:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">g.mapleader</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;,&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#e06c75">g.maplocalleader</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#34;;&#34;</span>
</span></span></code></pre></div><h3 id="buffer-jump">Buffer Jump</h3>
<p>With the awesome <a href="https://github.com/phaazon/hop.nvim">hop.nvim</a>, one can get hints to jump to various parts of a buffer (see the figure below if that does not make sense just yet). It is similiar to EasyMotions and supports jumps by line, word, characters sequences and pattern. However, in my opinion this plugin is not replacement for <code>w</code> and the like, because for short movements I find <code>w</code> or <code>f</code> shorter to type and because the need to read the screen for caracters may be quite slow compared to <code>3w</code>. It is also not a replacement for <code>/</code> because hop.nvim highlights whats visible on the current buffer. Thus it fills the void left for medium range move.</p>
<figure>
    <img loading="lazy" src="../hop-nvim-word.png"
         alt="I invoqued HopWord from line 105 and if I press dr, I’ll jump to the last word of line 116"/> <figcaption>
            Word jump example with hop.nvim<p>I invoqued <code>HopWord</code> from line 105 and if I press <code>dr</code>, I’ll jump to the last word of line 116</p>
        </figcaption>
</figure>

<p>Here is my configuration for this plugin:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-lua" data-lang="lua"><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;n&#39;</span>, <span style="color:#98c379">&#39;T&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopLineStart&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;v&#39;</span>, <span style="color:#98c379">&#39;T&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopLineStart&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;n&#39;</span>, <span style="color:#98c379">&#39;S&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopWord&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;v&#39;</span>, <span style="color:#98c379">&#39;S&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopWord&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;n&#39;</span>, <span style="color:#98c379">&#39;è&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopChar2&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;v&#39;</span>, <span style="color:#98c379">&#39;è&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopChar2&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;n&#39;</span>, <span style="color:#98c379">&#39;È&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopPattern&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">map</span>(<span style="color:#98c379">&#39;v&#39;</span>, <span style="color:#98c379">&#39;È&#39;</span>, <span style="color:#98c379">&#39;&lt;cmd&gt;HopPattern&lt;CR&gt;&#39;</span>)
</span></span><span style="display:flex;"><span><span style="color:#e06c75">require</span>(<span style="color:#98c379">&#39;hop&#39;</span>).<span style="color:#e06c75">setup</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#e06c75">keys</span> <span style="color:#56b6c2">=</span> <span style="color:#98c379">&#39;auietsrncbpovdljyxqghf&#39;</span>, <span style="color:#7f848e">-- Hint keys</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>It is heavily optimized for the the <a href="https://bepo.fr">BÉPO layout</a>, whose <a href="https://en.wikipedia.org/wiki/Touch_typing#Home_row">home row</a> contains the <code>auie</code> keys on the left hand and the <code>tsrn</code> keys on the right hand. Hence the choice to put most mappings on <code>T</code> and <code>S</code>, easily reachable with the right hand and to start the hint keys with <code>auie</code> on the left hand.</p>
<h2 id="plugins">Plugins</h2>
<p>These plugin are often mostly written in Lua, although vim script plugins can be more mature. The Lua-plugin ecosystem is moving really quickly, you may find that other plugins are more suitable in a couple months.</p>
<h3 id="plugins-i-use">Plugins I use</h3>
<h4 id="telescope">Telescope</h4>
<p>I use <a href="https://github.com/nvim-telescope/telescope.nvim">telescope.nvim</a> as a fuzzy finder. It can be slow at times compared to <a href="https://github.com/lotabout/skim.vim">skim</a> or <a href="https://github.com/junegunn/fzf.vim">fzf</a>. However, it is relatively easy to script in Lua, making it easy to tightly integrate with other functions.</p>
<h4 id="completion-nvim">Completion-nvim</h4>
<p>I use <a href="https://github.com/nvim-lua/completion-nvim">nvim-lua/completion-nvim</a> in particular for its chains of completion (several completion sources are chained and sources lower in the chain are used when upper sources don’t have any results). However, <a href="https://github.com/hrsh7th/nvim-compe">nvim-compe</a> may be a better choice if you start fresh.</p>
<h4 id="complete-list">Complete List</h4>
<p>This is a list of plugins I use, roughly ordered by frequency of use.</p>

<details>
  <summary>
    Click to show the list of plugins
  </summary>

<ul>
<li>
<p><a href="https://github.com/savq/paq-nvim">paq-nvim</a> a light package manager written in lua. It supports lazy loading, although it’s easier to do with <a href="https://github.com/wbthomason/packer.nvim">packer.nvim</a></p>
</li>
<li>
<p><a href="https://github.com/folke/which-key.nvim">folke/which-key.nvim</a> a great plugin to dispaly a popup with possible mappings. It also offer a register preview, a bit like <a href="https://github.com/tversteeg/registers.nvim">tversteeg/registers.nvim</a></p>
<ul>
<li><a href="https://github.com/kosayoda/nvim-lightbulb">kosayoda/nvim-lightbulb</a> displays a lightbulb when a code action is available</li>
</ul>
</li>
<li>
<p>Completions &amp; snippets:</p>
<ul>
<li><a href="https://github.com/neovim/nvim-lspconfig/">nvim-lspconfig</a> and <a href="https://github.com/neovim/nvim-lspconfig/blob/master/CONFIG.md">the guide for LSP Configurations</a> to use with the native LSP client in neovim.</li>
<li><a href="https://github.com/nvim-lua/completion-nvim">nvim-lua/completion-nvim</a> with these additional completion sources.
<ul>
<li><a href="https://github.com/nvim-treesitter/completion-treesitter">nvim-treesitter/completion-treesitter</a></li>
<li><a href="https://github.com/steelsojka/completion-buffers">steelsojka/completion-buffers</a></li>
</ul>
</li>
<li><a href="https://github.com/SirVer/ultisnips">SirVer/ultisnips</a> a well known snippets engine, see also <a href="https://github.com/honza/vim-snippets">honza/vim-snippets</a></li>
</ul>
</li>
<li>
<p>Mostly used with Git:</p>
<ul>
<li><a href="https://github.com/tpope/vim-fugitive">tpope/vim-fugitive</a> the well known git plugin
<ul>
<li><a href="https://github.com/tpope/vim-rhubarb">tpope/vim-rhubarb</a> :GBrowse for github</li>
</ul>
</li>
<li><a href="https://github.com/airblade/vim-gitgutter">airblade/vim-gitgutter</a> to see and manage git changes</li>
<li><a href="https://github.com/rhysd/conflict-marker.vim">rhysd/conflict-marker.vim</a> adds shortcuts to solve merge conflicts</li>
<li><a href="https://github.com/rhysd/committia.vim">rhysd/committia.vim</a> improves editing of commit messages with diff and status</li>
<li><a href="https://github.com/airblade/vim-rooter">airblade/vim-rooter</a> change to a project’s root directory</li>
</ul>
</li>
<li>
<p>Navigation:</p>
<ul>
<li><a href="https://github.com/phaazon/hop.nvim">phaazon/hop.nvim</a> a wonderful plugin that highlights text with short keystrokes to jump around</li>
<li><a href="https://github.com/rhysd/clever-f.vim">rhysd/clever-f.vim</a> &ndash; Better movements, frees “,” and “;” for <a href="#leaders">leaders</a></li>
<li><a href="https://github.com/arp242/jumpy.vim">arp242/jumpy.vim</a> allows to make big and precise jumps for instance from function to function</li>
</ul>
</li>
<li>
<p>Deal with symbols like <code>(</code> <code>[</code> or <code>&lt;</code></p>
<ul>
<li><a href="https://github.com/tpope/vim-surround">tpope/vim-surround</a> the well known plugin to add, change or delete symbols around text.</li>
<li><a href="https://github.com/jiangmiao/auto-pairs">jiangmiao/auto-pairs</a> closes pairs of symbols</li>
</ul>
</li>
<li>
<p><a href="https://github.com/ojroques/nvim-hardline">ojroques/nvim-hardline</a> a light and fast status line</p>
</li>
<li>
<p><a href="https://github.com/nvim-treesitter/nvim-treesitter">nvim-treesitter/nvim-treesitter</a>, one of the new big features of NeoVim 0.5. This is still in an early phase but it can already improve highlighting, refactoring and completions, sometimes with companion plugins like:</p>
<ul>
<li><a href="https://github.com/nvim-treesitter/nvim-treesitter-textobjects">nvim-treesitter/nvim-treesitter-textobjects</a></li>
<li><a href="https://github.com/nvim-treesitter/nvim-treesitter-refactor">nvim-treesitter/nvim-treesitter-refactor</a></li>
<li><a href="https://github.com/nvim-treesitter/playground">nvim-treesitter/playground</a></li>
</ul>
</li>
<li>
<p>Other convenience plugins</p>
<ul>
<li><a href="https://github.com/glepnir/indent-guides.nvim">glepnir/indent-guides.nvim</a> displays indentation guides.</li>
<li><a href="https://github.com/ojroques/nvim-bufdel">ojroques/nvim-bufdel</a> improve the deletion of buffers</li>
<li><a href="https://github.com/ojroques/vim-oscyank">ojroques/vim-oscyank</a> a that works across terminals, even over SSH</li>
<li><a href="https://github.com/winston0410/commented.nvim">winston0410/commented.nvim</a> (un)comment lines. It is the only lua comment plugin I’ve found that supports using <code>3gcc</code> to comment 3 lines,.</li>
<li><a href="https://github.com/dstein64/vim-startuptime">dstein64/vim-startuptime</a> a better interface to vim startup time profiles</li>
<li><a href="https://github.com/norcalli/nvim-colorizer.lua">norcalli/nvim-colorizer.lua</a> a plugin to highlight color codes with the right colors</li>
</ul>
</li>
<li>
<p><a href="https://github.com/nvim-telescope/telescope.nvim">nvim-telescope/telescope.nvim</a> a fuzzy finder with preview. I also use these extensions:</p>
<ul>
<li><a href="https://github.com/nvim-telescope/telescope-symbols.nvim">nvim-telescope/telescope-symbols.nvim</a></li>
<li><a href="https://github.com/fhill2/telescope-ultisnips.nvim">fhill2/telescope-ultisnips.nvim</a></li>
<li><a href="https://github.com/nvim-telescope/telescope-github.nvim">nvim-telescope/telescope-github.nvim</a></li>
</ul>
</li>
<li>
<p><a href="https://github.com/cj-rs/vim-bepo">cj-rs/vim-bepo</a> a customized fork of a plugin to adapt mappings to the <a href="https://bepo.fr/wiki/Accueil">bepo layout</a></p>
</li>
<li>
<p>If you work with more exotic syntaxes:</p>
<ul>
<li><a href="https://github.com/sheerun/vim-polyglot">sheerun/vim-polyglot</a></li>
<li><a href="https://github.com/cstrahan/vim-capnp">cstrahan/vim-capnp</a>

</details>
</li>
</ul>
</li>
</ul>
<h3 id="plugins-i-load-lazily">Plugins I load lazily</h3>
<p>I lazy load less often used plugins, so that they are not loaded at startup by default.</p>
<ul>
<li><a href="https://github.com/romgrk/nvim-treesitter-context">romgrk/nvim-treesitter-context</a> is a bit slow and buggy, but useful sometimes to get the context piled up at the top</li>
<li><a href="https://github.com/mattn/emmet-vim">mattn/emmet-vim</a> CSS abbreviations to generate HTML and other</li>
<li><a href="https://github.com/jbyuki/instant.nvim">jbyuki/instant.nvim</a> for remote pair programming</li>
</ul>
<h3 id="plugins-on-my-radar-for-the-future">Plugins on my Radar for the Future</h3>
<p>Plugins I might use in the future to replace existing pieces or add features:</p>
<ul>
<li><a href="https://github.com/puremourning/vimspector">vimspector</a> a debugger based on the DAP protocol (like LSP, but for debugguers)</li>
<li><a href="https://github.com/glepnir/lspsaga.nvim">lspsaga.nvim</a> a UI for the native LSP client</li>
<li><a href="https://github.com/hrsh7th/nvim-compe">nvim-compe</a> a completion engine with more advanced support for LSP. I quite like the chains in completion-nvim, but maybe nvim-compe could be a better fit at some point</li>
<li><a href="https://github.com/lambdalisue/gina.vim">gina.vim</a> to replace fugitive. It offers faster startup time and asynchronous git operations</li>
</ul>
<h2 id="other-interesting-configurations">Other Interesting Configurations</h2>
<ul>
<li><a href="https://github.com/tjdevries/config_manager/tree/master/xdg_config/nvim">tjdevries</a></li>
<li><a href="https://github.com/ojroques/dotfiles/tree/master/nvim">ojroques</a></li>
</ul>
<h2 id="thanks">Thanks</h2>
<p>Thanks to <a href="https://moverest.xyz">moverest</a> and <a href="https://jguer.space/">J. Guereiro</a> for reviewing drafts of this post.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>That was my experience at least and that of <a href="https://news.ycombinator.com/item?id=27715513">others</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Some parts of the NeoVim Lua API are not mature and you end up running some VimScript. But this is mostly true for things like mappings, which are closer to settings toggle than scripts. Even if you really want to define some mappings by scripting, you can mix Lua and VimScript like in <a href="#buffer-jump">buffer jump</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item></channel></rss>