bugfixes; update README.md

This commit is contained in:
2024-09-06 13:10:37 -06:00
parent 61460f0180
commit b9edc4d8ff
10 changed files with 366 additions and 58 deletions

157
README.md
View File

@@ -2,40 +2,123 @@
Welcome to **Text Tools (TT)** 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.
This is meant to be used as a **library**, not a plugin. On its own, text-tools.nvim does nothing on its own. 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.
This is meant to be used as a **library**, not a plugin. On its own, `text-tools.nvim` does nothing on its own. 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.
## Features
- **Range Utility**: Get context-aware selections with ease.
- **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.
### Installation
1. Clone the repository:
```bash
git clone https://github.com/yourusername/text-tools.git
```
2. Add the path to your `init.vim` or `init.lua`:
```lua
package.path = package.path .. ';/path/to/text-tools/lua/?.lua'
```
Lazy:
```lua
-- Setting `lazy = true` ensures that the library is only loaded
-- when `require 'tt.<utility>' is called.
{ 'jrop/text-tools.nvim', lazy = true }
```
## Usage
### A note on indices
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.
### 1. Creating a Range
To create a range, use the `Range.new(startPos, endPos, mode)` method:
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
local Range = require 'tt.range'
local startPos = Pos.new(0, 1, 0) -- Line 1, first column
local endPos = Pos.new(0, 3, 0) -- Line 3, first column
local myRange = Range.new(startPos, endPos)
local start = Pos.new(0, 0, 0) -- Line 1, first column
local stop = Pos.new(0, 2, 0) -- Line 3, first column
Range.new(start, stop, 'v') -- charwise selection
Range.new(start, stop, 'V') -- linewise selection
```
### 2. Working with Code Writer
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
-- get the first line in a buffer:
Range.from_line(0, 0)
-- Text Objects (any text object valid in your configuration is supported):
-- get the word the cursor is on:
Range.from_text_object('iw')
-- get the WORD the cursor is on:
Range.from_text_object('iW')
-- get the "..." the cursor is within:
Range.from_text_object('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()
--
-- Get the operated on text obtained from a motion:
-- (HINT: use the opkeymap utility to make this less verbose)
--
---@param ty 'char'|'line'|'block'
function MyOpFunc(ty)
local range = Range.from_op_func(ty)
-- do something with the range
end
-- Try invoking this with: `<Leader>toaw`, and the current word will be the context:
vim.keymap.set('<Leader>to', function()
vim.g.operatorfunc = 'v:lua.MyOpFunc'
return 'g@'
end, { expr = true })
--
-- Commands:
--
-- 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)
if range == nil then
-- the command was executed in normal mode
else
-- ...
end
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!
```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:line0(0) -- get the first line within this range
range:line0(-1) -- get the last line within this range
-- replace with new contents:
range:replace {
'replacement line 1',
'replacement line 2',
}
range:replace 'with a string'
-- delete the contents of the range:
range:replace(nil)
```
### 2. Defining Key Mappings over Motions
Define custom (dot-repeatable) key mappings for text objects:
```lua
local opkeymap = require 'tt.opkeymap'
-- invoke this function by typing, for example, `<leader>riw`:
-- `range` will contain the bounds of the motion `iw`.
opkeymap('n', '<leader>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:
@@ -44,35 +127,25 @@ local CodeWriter = require 'tt.codewriter'
local cw = CodeWriter.new()
cw:write('{')
cw:indent(function(innerCW)
innerCW:write('x: 123')
innerCW:write('x: 123')
end)
cw:write('}')
```
### 3. Defining Key Mappings
Define custom key mappings for text objects:
```lua
local opkeymap = require 'tt.opkeymap'
-- invoke this function by typing, for example, `<leader>riw`:
-- `range` will contain the bounds of the motion `iw`.
opkeymap('n', '<leader>r', function(range)
print(range:text()) -- Prints the text within the selected range
end)
```
### 4. Utility Functions
#### Cursor Position
#### Custom Text Objects
To manage cursor position, use the `Pos` class:
Simply by returning a `Range` or a `Pos`, you can easily and quickly define your own text objects:
```lua
local Pos = require 'tt.pos'
local cursorPos = Pos.new(0, 1, 5) -- Line 1, character 5
print(cursorPos:char()) -- Gets the character at the cursor position
local utils = require 'tt.utils'
local Range = require 'tt.range'
-- Select whole file:
utils.define_text_object('ag', function()
return Range.from_buf_text()
end)
```
#### Buffer Management
@@ -82,7 +155,19 @@ Access and manipulate buffers easily:
```lua
local Buffer = require 'tt.buffer'
local buf = Buffer.current()
print(buf:line_count()) -- Number of lines in the current buffer
buf:line_count() -- the number of lines in the current buffer
buf:get_option '...'
buf:set_option('...', ...)
buf:get_var '...'
buf:set_var('...', ...)
buf:all() -- returns a Range representing the entire buffer
buf:is_empty() -- returns true if the buffer has no text
buf:append_line '...'
buf:line0(0) -- returns a Range representing the first line in the buffer
buf:line0(-1) -- returns a Range representing the last line in the buffer
buf:lines(0, 1) -- returns a Range representing the first two lines in the buffer
buf:lines(1, -2) -- returns a Range representing all but the first and last lines of a buffer
buf:text_object('iw') -- returns a Range representing the text object 'iw' in the give buffer
```
## License (MIT)