diff --git a/README.md b/README.md
index 9999446..6f8822b 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,30 @@
# u.nvim
-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.
+Welcome to **u.nvim** - a Lua library for text manipulation in Neovim, focusing on
+range-based text operations, positions, and operator-pending mappings.
-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 a **single-file micro-library** meant to be vendored in your plugin or
+config. On its own, `u.nvim` does nothing. It is meant to be used by plugin
+authors to make their lives easier. To get an idea of what a plugin built on
+top of `u.nvim` would look like, check out the [examples/](./examples/) directory.
## 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)
-- **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.
-- **Operator Key Mapping**: Flexible key mapping that works with the selected
- text.
-- **Text and Position Utilities**: Convenient functions to manage text objects
- and cursor positions.
+- **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.
+- **Position Utilities**: Work with cursor positions, marks, and extmarks.
+- **Operator Key Mapping**: Flexible key mapping that works with motions.
+- **Text Object Definitions**: Define custom text objects easily.
+- **User Command Helpers**: Create commands with range support.
+- **Repeat Utilities**: Dot-repeat support for custom operations.
### Installation
-This being a library, and not a proper plugin, it is recommended that you
-vendor the specific version of this library that you need, including it in your
-code. Package managers are a developing landscape for Lua in the context of
-NeoVim. Perhaps in the future, `lux` will eliminate the need to vendor this
-library in your application code.
+This being a library, and not a proper plugin, it is recommended that you vendor
+the specific version of this library that you need, including it in your code.
+Package managers are a developing landscape for Lua in the context of NeoVim.
+Perhaps in the future, `lux` will eliminate the need to vendor this library in
+your application code.
#### If you are a Plugin Author
@@ -98,201 +88,6 @@ vim.pack.add { 'https://github.com/jrop/u.nvim' }
-## Signal and Rendering Usage
-
-### Overview
-
-The Signal and Rendering mechanisms are two subsystems of u.nvim, that, while
-simplistic, [compose](./examples/counter.lua) [together](./examples/filetree.lua)
-[powerfully](./examples/picker.lua) to create a system for interactive and
-responsive user interfaces. Here is a quick example that show-cases how easy it
-is to dive in to make any buffer an interactive UI:
-
-
-Example Code: counter.lua
-
-```lua
-local tracker = require 'u.tracker'
-local Buffer = require 'u.buffer'
-local h = require('u.renderer').h
-
--- Create an buffer for the UI
-vim.cmd.vnew()
-local ui_buf = Buffer.current()
-ui_buf:set_tmp_options()
-
-local s_count = tracker.create_signal(0)
-
--- Effect: Render
--- Setup the effect for rendering the UI whenever dependencies are updated
-tracker.create_effect(function()
- -- Calling `Signal:get()` in an effect registers the given signal as a
- -- dependency of the current effect. Whenever that signal (or any other
- -- dependency) changes, the effect will rerun. In this particular case,
- -- rendering the UI is an effect that depends on one signal.
- local count = s_count:get()
-
- -- Markup is hyperscript, which is just 1) text, and 2) tags (i.e.,
- -- constructed with `h(...)` calls). To help organize the markup, text and
- -- tags can be nested in tables at any depth. Line breaks must be specified
- -- manually, with '\n'.
- ui_buf:render {
- 'Reactive Counter Example\n',
- '========================\n\n',
-
- { 'Counter: ', tostring(count), '\n' },
-
- '\n',
-
- {
- h('text', {
- hl = 'DiffDelete',
- nmap = {
- [''] = function()
- -- Update the contents of the s_count signal, notifying any
- -- dependencies (in this case, the render effect):
- vim.schedule(function()
- s_count:update(function(n) return n - 1 end)
- end)
- -- Also equivalent: s_count:set(s_count:get() - 1)
- return ''
- end,
- },
- }, ' Decrement '),
- ' ',
- h('text', {
- hl = 'DiffAdd',
- nmap = {
- [''] = function()
- -- Update the contents of the s_count signal, notifying any
- -- dependencies (in this case, the render effect):
- vim.schedule(function()
- s_count:update(function(n) return n + 1 end)
- end)
- -- Also equivalent: s_count:set(s_count:get() - 1)
- return ''
- end,
- },
- }, ' Increment '),
- },
-
- '\n',
- '\n',
- { 'Press on each "button" above to increment/decrement the counter.' },
- }
-end)
-```
-
-
-
-### `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.
-
-```lua
-local tracker = require('u.tracker')
-
-local s_number = tracker.Signal:new(0)
--- auto-compute the double of the number each time it changes:
-local s_doubled = tracker.create_memo(function() return s_number:get() * 2 end)
-tracker.create_effect(function()
- local n = s_doubled:get()
- -- ...
- -- whenever s_doubled changes, this function gets run
-end)
-```
-
-**Note**: circular dependencies are **not** supported.
-
-### `u.renderer`
-
-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
-desired state.
-
-**Hyperscript** is just 1) _text_ 2) `` tags, which can be nested in 3)
-Lua tables for readability:
-
-```lua
-local h = require('u.renderer').h
--- Hyperscript can be organized into tables:
-{
- "Hello, ",
- {
- "I am ", { "a" }, " nested table.",
- },
- '\n', -- newlines must be explicitly specified
-
- -- booleans/nil are ignored:
- some_conditional_flag and 'This text only shows when the flag is true',
- -- e.g., use the above to show newlines in lists:
- idx > 1 and '\n',
-
- -- tags are specified like so:
- -- h('text', attributes, children)
- h('text', {}, "I am a text node."),
-
- -- tags can be highlighted:
- h('text', { hl = 'Comment' }, "I am highlighted."),
-
- -- tags can respond to key events:
- h('text', {
- hl = 'Keyword',
- nmap = {
- [""] = function()
- print("Hello World")
- -- Return '' to swallow the event:
- return ''
- end,
- },
- }, "I am a text node."),
-}
-```
-
-Managing complex tables of hyperscript can be done more ergonomically using the
-`TreeBuilder` helper class:
-
-```lua
-local TreeBuilder = require('u.renderer').TreeBuilder
-
--- ...
-renderer:render(
- TreeBuilder.new()
- -- text:
- :put('some text')
- -- hyperscript tables:
- :put({ 'some text', 'more hyperscript' })
- -- hyperscript tags:
- :put_h('text', { --[[attributes]] }, { --[[children]] })
- -- callbacks:
- --- @param tb TreeBuilder
- :nest(function(tb)
- tb:put('some text')
- end)
- :tree()
-)
-```
-
-**Rendering**: The renderer library provides a `render` function that takes
-hyperscript in, and converts it to formatted buffer text:
-
-```lua
-local Renderer = require('u.renderer').Renderer
-local renderer = Renderer:new(0 --[[buffer number]])
-renderer:render {
- -- ...hyperscript...
-}
-
--- or, if you already have a buffer:
-local Buffer = require('u.buffer')
-local buf = Buffer.current()
-buf:render {
- -- ...hyperscript...
-}
-```
-
## Range Usage
### A note on indices
@@ -327,17 +122,17 @@ interop conversions when calling `:api` functions.
### 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 of this library. Ranges can be constructed
+manually, or preferably, obtained based on a variety of contexts.
```lua
-local Range = require 'u.range'
-local start = Pos.new(0, 1, 1) -- Line 1, first column
-local stop = Pos.new(0, 3, 1) -- Line 3, first column
+local u = require 'u'
-Range.new(start, stop, 'v') -- charwise selection
-Range.new(start, stop, 'V') -- linewise selection
+local start = u.Pos.new(nil, 1, 1) -- Line 1, first column
+local stop = u.Pos.new(nil, 3, 1) -- Line 3, first column
+
+u.Range.new(start, stop, 'v') -- charwise selection
+u.Range.new(start, stop, 'V') -- linewise selection
```
This is usually not how you want to obtain a `Range`, however. Usually you want
@@ -345,21 +140,23 @@ to get the corresponding context of an edit operation and just "get me the
current Range that represents this context".
```lua
+local u = require 'u'
+
-- get the first line in a buffer:
-Range.from_line(bufnr, 1)
+u.Range.from_line(bufnr, 1)
-- Text Objects (any text object valid in your configuration is supported):
-- get the word the cursor is on:
-Range.from_motion('iw')
+u.Range.from_motion('iw')
-- get the WORD the cursor is on:
-Range.from_motion('iW')
+u.Range.from_motion('iW')
-- get the "..." the cursor is within:
-Range.from_motion('a"')
+u.Range.from_motion('a"')
-- Get the currently visually selected text:
-- NOTE: this does NOT work within certain contexts; more specialized utilities
-- are more appropriate in certain circumstances
-Range.from_vtext()
+u.Range.from_vtext()
--
-- Get the operated on text obtained from a motion:
@@ -367,7 +164,7 @@ Range.from_vtext()
--
--- @param ty 'char'|'line'|'block'
function MyOpFunc(ty)
- local range = Range.from_op_func(ty)
+ local range = u.Range.from_op_func(ty)
-- do something with the range
end
-- Try invoking this with: `toaw`, and the current word will be the
@@ -383,7 +180,7 @@ end, { expr = true })
-- 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)
- local range = Range.from_cmd_args(args)
+ local range = u.Range.from_cmd_args(args)
if range == nil then
-- the command was executed in normal mode
else
@@ -398,7 +195,7 @@ range once you have one? Plenty, it turns out!
```lua
local range = ...
range:lines() -- get the lines in the range's region
-range:text() -- get the text (i.e., string) in the range's region
+range:text() -- get the text (i.e., string) in the range's region
range:line(1) -- get the first line within this range
range:line(-1) -- get the last line within this range
-- replace with new contents:
@@ -416,68 +213,125 @@ range:replace(nil)
Define custom (dot-repeatable) key mappings for text objects:
```lua
-local opkeymap = require 'u.opkeymap'
+local u = require 'u'
-- invoke this function by typing, for example, `riw`:
-- `range` will contain the bounds of the motion `iw`.
-opkeymap('n', 'r', function(range)
+u.opkeymap('n', 'r', function(range)
print(range:text()) -- Prints the text within the selected range
end)
```
-### 3. Working with Code Writer
-
-To write code with indentation, use the `CodeWriter` class:
-
-```lua
-local CodeWriter = require 'u.codewriter'
-local cw = CodeWriter.new()
-cw:write('{')
-cw:indent(function(innerCW)
- innerCW:write('x: 123')
-end)
-cw:write('}')
-```
-
-### 4. Utility Functions
+### 3. Utility Functions
#### Custom Text Objects
-Simply by returning a `Range` or a `Pos`, you can easily and quickly define
-your own text objects:
+Simply by returning a `Range`, you can easily define your own text objects:
```lua
-local txtobj = require 'u.txtobj'
-local Range = require 'u.range'
+local u = require 'u'
-- Select whole file:
-txtobj.define('ag', function()
- return Range.from_buf_text()
+u.define_txtobj('ag', function()
+ return u.Range.from_buf_text()
+end)
+
+-- Select content inside nearest quotes:
+u.define_txtobj('iq', function()
+ return u.Range.find_nearest_quotes()
+end)
+
+-- Select content inside nearest brackets:
+u.define_txtobj('ib', function()
+ return u.Range.find_nearest_brackets()
end)
```
-#### Buffer Management
+#### User Commands with Range Support
-Access and manipulate buffers easily:
+Create user commands that work with visual selections:
```lua
-local Buffer = require 'u.buffer'
-local buf = Buffer.current()
-buf.b.