update README
Some checks failed
NeoVim tests / plenary-tests (push) Failing after 7s

This commit is contained in:
Jonathan Apodaca 2025-04-13 21:57:51 -06:00
parent 466698a9de
commit e8d19e85c3

111
README.md
View File

@ -1,26 +1,40 @@
# u.nvim # u.nvim
Welcome to **u.nvim** a powerful Lua library designed to enhance your text manipulation experience in NeoVim, focusing primarily on a context-aware "Range" utility. This utility allows you to work efficiently with text selections based on various conditions, in a variety of contexts, making coding and editing more intuitive and productive. Welcome to **u.nvim** - a powerful Lua library designed to enhance your text
manipulation experience in NeoVim, focusing on text-manipulation utilities.
This includes a `Range` utility, allowing you to work efficiently with text
selections based on various conditions, as well as a declarative `Render`-er,
making coding and editing more intuitive and productive.
This is meant to be used as a **library**, not a plugin. On its own, `u.nvim` does nothing. It is meant to be used by plugin authors, to make their lives easier based on the variety of utilities I found I needed while growing my NeoVim config. To get an idea of what a plugin built on top of `u.nvim` would look like, check out the [examples/](./examples/) directory. This is meant to be used as a **library**, not a plugin. On its own, `u.nvim`
does nothing. It is meant to be used by plugin authors, to make their lives
easier based on the variety of utilities I found I needed while growing my
NeoVim config. To get an idea of what a plugin built on top of `u.nvim` would
look like, check out the [examples/](./examples/) directory.
## Features ## Features
- **Rendering System**: a utility that can declaratively render NeoVim-specific hyperscript into a buffer, supporting creating/managing extmarks, highlights, and key-event handling (requires NeoVim >0.11) - **Rendering System**: a utility that can declaratively render NeoVim-specific
- **Signals**: a simple dependency tracking system that pairs well with the rendering utilities for creating reactive/interactive UIs in NeoVim. hyperscript into a buffer, supporting creating/managing extmarks, highlights,
- **Range Utility**: Get context-aware selections with ease. Replace regions with new text. Think of it as a programmatic way to work with visual selections (or regions of text). and key-event handling (requires NeoVim >0.11)
- **Signals**: a simple dependency tracking system that pairs well with the
rendering utilities for creating reactive/interactive UIs in NeoVim.
- **Range Utility**: Get context-aware selections with ease. Replace regions
with new text. Think of it as a programmatic way to work with visual
selections (or regions of text).
- **Code Writer**: Write code with automatic indentation and formatting. - **Code Writer**: Write code with automatic indentation and formatting.
- **Operator Key Mapping**: Flexible key mapping that works with the selected text. - **Operator Key Mapping**: Flexible key mapping that works with the selected
- **Text and Position Utilities**: Convenient functions to manage text objects and cursor positions. text.
- **Text and Position Utilities**: Convenient functions to manage text objects
and cursor positions.
### Installation ### Installation
lazy.nvim: This being a library, and not a proper plugin, it is recommended that you
```lua vendor the specific version of this library that you need, including it in your
-- Setting `lazy = true` ensures that the library is only loaded code. Package managers are a developing landscape for Lua in the context of
-- when `require 'u.<utility>' is called. NeoVim. Perhaps in the future, `lux` will eliminate the need to vendor this
{ 'jrop/u.nvim', lazy = true } library in your application code.
```
## Signal and Rendering Usage ## Signal and Rendering Usage
@ -111,7 +125,9 @@ end)
### `u.tracker` ### `u.tracker`
The `u.tracker` module provides a simple API for creating reactive variables. These can be composed in Effects and Memos utilizing Execution Contexts that track what signals are used by effects/memos. The `u.tracker` module provides a simple API for creating reactive variables.
These can be composed in Effects and Memos utilizing Execution Contexts that
track what signals are used by effects/memos.
```lua ```lua
local tracker = require('u.tracker') local tracker = require('u.tracker')
@ -134,7 +150,8 @@ The renderer library renders hyperscript into a buffer. Each render performs a
minimal set of changes in order to transform the current buffer text into the minimal set of changes in order to transform the current buffer text into the
desired state. desired state.
**Hyperscript** is just 1) _text_ 2) `<text>` tags, which can be nested in 3) Lua tables for readability: **Hyperscript** is just 1) _text_ 2) `<text>` tags, which can be nested in 3)
Lua tables for readability:
```lua ```lua
local h = require('u.renderer').h local h = require('u.renderer').h
@ -196,7 +213,8 @@ renderer:render(
) )
``` ```
**Rendering**: The renderer library provides a `render` function that takes hyperscript in, and converts it to formatted buffer text: **Rendering**: The renderer library provides a `render` function that takes
hyperscript in, and converts it to formatted buffer text:
```lua ```lua
local Renderer = require('u.renderer').Renderer local Renderer = require('u.renderer').Renderer
@ -219,22 +237,37 @@ buf:render {
<blockquote> <blockquote>
<del> <del>
I love NeoVim. I am coming to love Lua. I don't like 1-based indices; perhaps I am too old. Perhaps I am too steeped in the history of loving the elegance of simple pointer arithmetic. Regardless, the way positions are addressed in NeoVim/Vim is (terrifyingly) mixed. Some methods return 1-based, others accept only 0-based. In order to stay sane, I had to make a choice to store everything in one, uniform representation in this library. I chose (what I humbly think is the only sane way) to stick with the tried-and-true 0-based index scheme. That abstraction leaks into the public API of this library. I love NeoVim. I am coming to love Lua. I don't like 1-based indices; perhaps I
am too old. Perhaps I am too steeped in the history of loving the elegance of
simple pointer arithmetic. Regardless, the way positions are addressed in
NeoVim/Vim is (terrifyingly) mixed. Some methods return 1-based, others accept
only 0-based. In order to stay sane, I had to make a choice to store everything
in one, uniform representation in this library. I chose (what I humbly think is
the only sane way) to stick with the tried-and-true 0-based index scheme. That
abstraction leaks into the public API of this library.
</del> </del>
</blockquote> </blockquote>
<br /> <br />
<b>This has changed in v2</b>. After much thought, I realized that: <b>This has changed in v2</b>. After much thought, I realized that:
1. The 0-based indexing in NeoVim is prevelant in the `:api`, which is designed to be exposed to many languages. As such, it makes sense for this interface to use 0-based indexing. However, many internal Vim functions use 1-based indexing. 1. The 0-based indexing in NeoVim is prevelant in the `:api`, which is designed
2. This is a Lua library (surprise, surprise, duh) - the idioms of the language should take precedence over my preference to be exposed to many languages. As such, it makes sense for this interface
3. There were subtle bugs in the code where indices weren't being normalized to 0-based, anyways. Somehow it worked most of the time. to use 0-based indexing. However, many internal Vim functions use 1-based
indexing.
2. This is a Lua library (surprise, surprise, duh) - the idioms of the language
should take precedence over my preference
3. There were subtle bugs in the code where indices weren't being normalized to
0-based, anyways. Somehow it worked most of the time.
As such, this library now uses 1-based indexing everywhere, doing the necessary interop conversions when calling `:api` functions. As such, this library now uses 1-based indexing everywhere, doing the necessary
interop conversions when calling `:api` functions.
### 1. Creating a Range ### 1. Creating a Range
The `Range` utility is the main feature upon which most other things in this library are built, aside from a few standalone utilities. Ranges can be constructed manually, or preferably, obtained based on a variety of contexts. The `Range` utility is the main feature upon which most other things in this
library are built, aside from a few standalone utilities. Ranges can be
constructed manually, or preferably, obtained based on a variety of contexts.
```lua ```lua
local Range = require 'u.range' local Range = require 'u.range'
@ -245,7 +278,9 @@ Range.new(start, stop, 'v') -- charwise selection
Range.new(start, stop, 'V') -- linewise selection Range.new(start, stop, 'V') -- linewise selection
``` ```
This is usually not how you want to obtain a `Range`, however. Usually you want to get the corresponding context of an edit operation and just "get me the current Range that represents this context". This is usually not how you want to obtain a `Range`, however. Usually you want
to get the corresponding context of an edit operation and just "get me the
current Range that represents this context".
```lua ```lua
-- get the first line in a buffer: -- get the first line in a buffer:
@ -260,7 +295,8 @@ Range.from_motion('iW')
Range.from_motion('a"') Range.from_motion('a"')
-- Get the currently visually selected text: -- Get the currently visually selected text:
-- NOTE: this does NOT work within certain contexts; more specialized utilities are more appropriate in certain circumstances -- NOTE: this does NOT work within certain contexts; more specialized utilities
-- are more appropriate in certain circumstances
Range.from_vtext() Range.from_vtext()
-- --
@ -272,7 +308,8 @@ function MyOpFunc(ty)
local range = Range.from_op_func(ty) local range = Range.from_op_func(ty)
-- do something with the range -- do something with the range
end end
-- Try invoking this with: `<Leader>toaw`, and the current word will be the context: -- Try invoking this with: `<Leader>toaw`, and the current word will be the
-- context:
vim.keymap.set('<Leader>to', function() vim.keymap.set('<Leader>to', function()
vim.g.operatorfunc = 'v:lua.MyOpFunc' vim.g.operatorfunc = 'v:lua.MyOpFunc'
return 'g@' return 'g@'
@ -281,7 +318,8 @@ end, { expr = true })
-- --
-- Commands: -- Commands:
-- --
-- When executing commands in a visual context, getting the selected text has to be done differently: -- When executing commands in a visual context, getting the selected text has
-- to be done differently:
vim.api.nvim_create_user_command('MyCmd', function(args) vim.api.nvim_create_user_command('MyCmd', function(args)
local range = Range.from_cmd_args(args) local range = Range.from_cmd_args(args)
if range == nil then if range == nil then
@ -292,7 +330,8 @@ vim.api.nvim_create_user_command('MyCmd', function(args)
end, { range = true }) end, { range = true })
``` ```
So far, that's a lot of ways to _get_ a `Range`. But what can you do with a range once you have one? Plenty, it turns out! So far, that's a lot of ways to _get_ a `Range`. But what can you do with a
range once you have one? Plenty, it turns out!
```lua ```lua
local range = ... local range = ...
@ -381,8 +420,20 @@ buf:txtobj('iw') -- returns a Range representing the text object 'iw' in the giv
Copyright (c) 2024 jrapodaca@gmail.com Copyright (c) 2024 jrapodaca@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.