This is an automated archive made by the Lemmit Bot.

The original was posted on /r/neovim by /u/jrop2 on 2024-11-09 15:04:19+00:00.


TL;DR*:* u.nvim is a library, not a plugin. It is a set of utilities that make programmatic text-manipulation, specifically within the context of NeoVim easier (e.g., you can do things like Range.from_text_object ‘iw’:replace ‘foo’). If you write lots of Lua for NeoVim, chances are that you will love u.nvim.

First, the link: u.nvim.

Hello NeoVim users. I’m excited to finally release something that I’ve had in the works for over 6 months now-- and it’s not a plugin (?)! If you’re confused, keep reading, because it will all make sense eventually…

Back in March of this year, I decided to conduct an experiment: what would my config look like if I did not allow myself any plugins? I quickly created a scratch config, and began hacking away. What started out as an experiment quickly cascaded into a journey of discovery as I learned more and more about the internals of NeoVim. To spare you the details, I ended up implementing my own splitjoin plugin, followed by my own surround plugin (both support dot-repeat, and I use both every single day), plus a bunch of other stuff I deemed “essential”. Slowly, I was able to whittle down on my plugin list for my full, daily-driver config, as I replaced each piece with something I had homegrown.

As I progressed in my journey, I found myself extracting little NeoVim-specific utilities, and one particular abstraction emerged as fundamental: a “Range”. This “Range” utility started out as a representation for visual selection, but it grew to represent any region of text in a buffer. I began cataloging different ways Ranges could be obtained:

  • Range.from_cmd_args(...) - for getting the selected text when a command is invoked on a range (i.e., :'<,'>MyCommand)
  • Range.from_op_func(...) - write your own operator functions and get the operated-on text easily
  • Range.from_vtext() - get the currently selected text
  • Range.from_text_object('iw') - get the text object under the cursor (or from a given position)
  • Range.find_nearest_brackets() - find the nearest surrounding brackets
  • Range.find_nearest_quotes() - find the nearest surrounding quotes

Ranges are the most fully-fleshed out API of u.nvim, mostly because they are the most useful for my use. They are also used as a fundamental building-block for other utilities in the library. For example, you can easily define your own programmatic text-object:

utils.define_text_object('ag', function()
  return Range.from_buf_text()
end)

You can easily manipulate the text in ranges:

local range = Range.from_text_object('iw')
range:text() -- get the text in the range as a single string
range:lines() -- get the text in the range as a list of lines (i.e, string[])
range:replace('foo')
-- or replace with multiple lines: 
range:replace({ 'foo', 'bar' })
-- or delete text in the range:
range:replace(nil)

There are other utilities – for that the README is a better reference. Chances are, only a small percentage of you will find this useful, but for that subset, I hope this library saves you the hassle of writing a lot of special-case code. Alternatively, it may serve as a reference for the things I ended up learning on my journey.

One more thing: I’m really grateful for the inclusion of Lua as a first-class citizen in NeoVim. I never would have been able to write this library without other plugins serving as a reference for how to get certain things done in Vim. I peered under the hood of tpope’s plugins, and while revealing (I was largely enlightened by tpope’s surround/repeat plugins, and for that I am eternally grateful), nothing was more approachable than being able to dive into Lua-hosted modules, and see what I was after in an infinitely more approachable way.

So there you have it. It’s still early days for u.nvim, so Issues/PRs are welcome.