bugfixes; update README.md
This commit is contained in:
parent
61460f0180
commit
b9edc4d8ff
15
.github/workflows/ci.yaml
vendored
Normal file
15
.github/workflows/ci.yaml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json
|
||||
name: NeoVim tests
|
||||
on: [push]
|
||||
jobs:
|
||||
plenary-tests:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
XDG_CONFIG_HOME: ${{ github.workspace }}/.config/
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: rhysd/action-setup-vim@v1
|
||||
with:
|
||||
neovim: true
|
||||
version: v0.10.1
|
||||
- run: make test
|
157
README.md
157
README.md
@ -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)
|
||||
|
@ -1,4 +1,6 @@
|
||||
local function withbuf(lines, f)
|
||||
vim.opt_global.swapfile = false
|
||||
|
||||
vim.cmd.new()
|
||||
vim.api.nvim_buf_set_lines(0, 0, -1, false, lines)
|
||||
local ok, result = pcall(f)
|
||||
|
@ -6,7 +6,7 @@ local Buffer = {}
|
||||
|
||||
---@param buf? number
|
||||
---@return Buffer
|
||||
function Buffer.new(buf)
|
||||
function Buffer.from_nr(buf)
|
||||
if buf == nil or buf == 0 then buf = vim.api.nvim_get_current_buf() end
|
||||
local b = { buf = buf }
|
||||
setmetatable(b, { __index = Buffer })
|
||||
@ -14,12 +14,12 @@ function Buffer.new(buf)
|
||||
end
|
||||
|
||||
---@return Buffer
|
||||
function Buffer.current() return Buffer.new(0) end
|
||||
function Buffer.current() return Buffer.from_nr(0) end
|
||||
|
||||
---@param listed boolean
|
||||
---@param scratch boolean
|
||||
---@return Buffer
|
||||
function Buffer.create(listed, scratch) return Buffer.new(vim.api.nvim_create_buf(listed, scratch)) end
|
||||
function Buffer.create(listed, scratch) return Buffer.from_nr(vim.api.nvim_create_buf(listed, scratch)) end
|
||||
|
||||
function Buffer:set_tmp_options()
|
||||
self:set_option('bufhidden', 'delete')
|
||||
|
@ -24,7 +24,7 @@ end
|
||||
|
||||
---@param p Pos
|
||||
function CodeWriter.from_pos(p)
|
||||
local line = Buffer.new(p.buf):line0(p.lnum):text()
|
||||
local line = Buffer.from_nr(p.buf):line0(p.lnum):text()
|
||||
return CodeWriter.from_line(line, p.buf)
|
||||
end
|
||||
|
||||
@ -55,8 +55,6 @@ end
|
||||
---@param line string
|
||||
function CodeWriter:write_raw(line)
|
||||
if line:find '\n' then error 'line contains newline character' end
|
||||
line = line:gsub('^\n+', '')
|
||||
line = line:gsub('\n+$', '')
|
||||
table.insert(self.lines, line)
|
||||
end
|
||||
|
||||
|
@ -2,20 +2,20 @@ local Range = require 'tt.range'
|
||||
local vim_repeat = require 'tt.repeat'
|
||||
|
||||
---@type fun(range: Range): nil|(fun():any)
|
||||
local MyOpKeymapOpFunc_rhs = nil
|
||||
local __TT__OpKeymapOpFunc_rhs = nil
|
||||
|
||||
--- This is the global utility function used for operatorfunc
|
||||
--- in opkeymap
|
||||
---@type nil|fun(range: Range): fun():any|nil
|
||||
---@param ty 'line'|'char'|'block'
|
||||
-- selene: allow(unused_variable)
|
||||
function MyOpKeymapOpFunc(ty)
|
||||
if MyOpKeymapOpFunc_rhs ~= nil then
|
||||
function __TT__OpKeymapOpFunc(ty)
|
||||
if __TT__OpKeymapOpFunc_rhs ~= nil then
|
||||
local range = Range.from_op_func(ty)
|
||||
local repeat_inject = MyOpKeymapOpFunc_rhs(range)
|
||||
local repeat_inject = __TT__OpKeymapOpFunc_rhs(range)
|
||||
|
||||
vim_repeat.set(function()
|
||||
vim.o.operatorfunc = 'v:lua.MyOpKeymapOpFunc'
|
||||
vim.o.operatorfunc = 'v:lua.__TT__OpKeymapOpFunc'
|
||||
if repeat_inject ~= nil and type(repeat_inject) == 'function' then repeat_inject() end
|
||||
vim_repeat.native_repeat()
|
||||
end)
|
||||
@ -34,8 +34,8 @@ end
|
||||
---@param opts? vim.keymap.set.Opts
|
||||
local function opkeymap(mode, lhs, rhs, opts)
|
||||
vim.keymap.set(mode, lhs, function()
|
||||
MyOpKeymapOpFunc_rhs = rhs
|
||||
vim.o.operatorfunc = 'v:lua.MyOpKeymapOpFunc'
|
||||
__TT__OpKeymapOpFunc_rhs = rhs
|
||||
vim.o.operatorfunc = 'v:lua.__TT__OpKeymapOpFunc'
|
||||
return 'g@'
|
||||
end, vim.tbl_extend('force', opts or {}, { expr = true }))
|
||||
end
|
||||
|
@ -78,6 +78,10 @@ function Range.from_line(buf, line) return Range.from_lines(buf, line, line) end
|
||||
---@param stop_line number 0-based line index
|
||||
function Range.from_lines(buf, start_line, stop_line)
|
||||
if buf == nil or buf == 0 then buf = vim.api.nvim_get_current_buf() end
|
||||
if stop_line < 0 then
|
||||
local num_lines = vim.api.nvim_buf_line_count(buf)
|
||||
stop_line = num_lines + stop_line
|
||||
end
|
||||
return Range.new(Pos.new(buf, start_line, 0), Pos.new(buf, stop_line, Pos.MAX_COL), 'V')
|
||||
end
|
||||
|
||||
@ -237,7 +241,7 @@ function Range.smallest(ranges)
|
||||
end
|
||||
|
||||
function Range:clone() return Range.new(self.start:clone(), self.stop:clone(), self.mode) end
|
||||
function Range:line_count() return self.stop.lnum + self.start.lnum + 1 end
|
||||
function Range:line_count() return self.stop.lnum - self.start.lnum + 1 end
|
||||
|
||||
function Range:to_linewise()
|
||||
local r = self:clone()
|
||||
@ -369,7 +373,7 @@ end
|
||||
---@param amount number
|
||||
function Range:must_shrink(amount)
|
||||
local shrunk = self:shrink(amount)
|
||||
if shrunk == nil then error 'error in Range:must_shrink: Range:shrink() returned nil' end
|
||||
if shrunk == nil or shrunk:is_empty() then error 'error in Range:must_shrink: Range:shrink() returned nil' end
|
||||
return shrunk
|
||||
end
|
||||
|
||||
@ -397,12 +401,8 @@ function Range:set_visual_selection()
|
||||
self.start:save_to_mark 'a'
|
||||
self.stop:save_to_mark 'b'
|
||||
local mode = self.mode
|
||||
if vim.api.nvim_get_mode().mode == 'n' then
|
||||
vim.cmd.normal { cmd = 'normal', args = { '`a' .. mode .. '`b' }, bang = true }
|
||||
else
|
||||
utils.feedkeys '`ao`b'
|
||||
end
|
||||
|
||||
if vim.api.nvim_get_mode().mode == 'n' then utils.feedkeys(mode) end
|
||||
utils.feedkeys '`ao`b'
|
||||
return nil
|
||||
end)
|
||||
end
|
||||
|
@ -4,10 +4,27 @@ local withbuf = require '__tt_test_tools'
|
||||
describe('Buffer', function()
|
||||
it('should replace all lines', function()
|
||||
withbuf({}, function()
|
||||
local buf = Buffer.new()
|
||||
local buf = Buffer.from_nr()
|
||||
buf:all():replace 'bleh'
|
||||
local actual_lines = vim.api.nvim_buf_get_lines(buf.buf, 0, -1, false)
|
||||
assert.are.same({ 'bleh' }, actual_lines)
|
||||
end)
|
||||
end)
|
||||
|
||||
it('should replace all but first and last lines', function()
|
||||
withbuf({
|
||||
'one',
|
||||
'two',
|
||||
'three',
|
||||
}, function()
|
||||
local buf = Buffer.from_nr()
|
||||
buf:lines(1, -2):replace 'too'
|
||||
local actual_lines = vim.api.nvim_buf_get_lines(buf.buf, 0, -1, false)
|
||||
assert.are.same({
|
||||
'one',
|
||||
'too',
|
||||
'three',
|
||||
}, actual_lines)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
@ -276,4 +276,183 @@ describe('Range', function()
|
||||
assert.are.same('block', range:line0(1).text())
|
||||
end)
|
||||
end)
|
||||
|
||||
it('from_marks', function()
|
||||
withbuf({ 'line one', 'and line two' }, function()
|
||||
local a = Pos.new(nil, 0, 0)
|
||||
local b = Pos.new(nil, 1, 1)
|
||||
a:save_to_pos "'["
|
||||
b:save_to_pos "']"
|
||||
|
||||
local range = Range.from_marks("'[", "']")
|
||||
assert.are.same(range.start, a)
|
||||
assert.are.same(range.stop, b)
|
||||
assert.are.same(range.mode, 'v')
|
||||
end)
|
||||
end)
|
||||
|
||||
it('from_vtext', function()
|
||||
withbuf({ 'line one', 'and line two' }, function()
|
||||
vim.fn.setpos('.', { 0, 1, 3, 0 }) -- cursor at position (1, 3)
|
||||
vim.cmd.normal 'v' -- enter visual mode
|
||||
vim.cmd.normal 'l' -- select one character to the right
|
||||
local range = Range.from_vtext()
|
||||
assert.are.same(range.start, Pos.new(nil, 0, 2))
|
||||
assert.are.same(range.stop, Pos.new(nil, 0, 3))
|
||||
assert.are.same(range.mode, 'v')
|
||||
end)
|
||||
end)
|
||||
|
||||
it('from_op_func', function()
|
||||
withbuf({ 'line one', 'and line two' }, function()
|
||||
local a = Pos.new(nil, 0, 0)
|
||||
local b = Pos.new(nil, 1, 1)
|
||||
a:save_to_pos "'["
|
||||
b:save_to_pos "']"
|
||||
|
||||
local range = Range.from_op_func 'char'
|
||||
assert.are.same(range.start, a)
|
||||
assert.are.same(range.stop, b)
|
||||
assert.are.same(range.mode, 'v')
|
||||
|
||||
range = Range.from_op_func 'line'
|
||||
assert.are.same(range.start, a)
|
||||
assert.are.same(range.stop, Pos.new(nil, 1, Pos.MAX_COL))
|
||||
assert.are.same(range.mode, 'V')
|
||||
end)
|
||||
end)
|
||||
|
||||
it('from_cmd_args', function()
|
||||
local args = { range = 1 }
|
||||
withbuf({ 'line one', 'and line two' }, function()
|
||||
local a = Pos.new(nil, 0, 0)
|
||||
local b = Pos.new(nil, 1, 1)
|
||||
a:save_to_pos "'<"
|
||||
b:save_to_pos "'>"
|
||||
|
||||
local range = Range.from_cmd_args(args)
|
||||
assert.are.same(range.start, a)
|
||||
assert.are.same(range.stop, b)
|
||||
assert.are.same(range.mode, 'v')
|
||||
end)
|
||||
end)
|
||||
|
||||
it('find_nearest_quotes', function()
|
||||
withbuf({ [[the "quick" brown fox]] }, function()
|
||||
vim.fn.setpos('.', { 0, 1, 5, 0 })
|
||||
local range = Range.find_nearest_quotes()
|
||||
assert.are.same(range.start, Pos.new(nil, 0, 4))
|
||||
assert.are.same(range.stop, Pos.new(nil, 0, 10))
|
||||
end)
|
||||
|
||||
withbuf({ [[the 'quick' brown fox]] }, function()
|
||||
vim.fn.setpos('.', { 0, 1, 5, 0 })
|
||||
local range = Range.find_nearest_quotes()
|
||||
assert.are.same(range.start, Pos.new(nil, 0, 4))
|
||||
assert.are.same(range.stop, Pos.new(nil, 0, 10))
|
||||
end)
|
||||
end)
|
||||
|
||||
it('smallest', function()
|
||||
local r1 = Range.new(Pos.new(nil, 0, 1), Pos.new(nil, 0, 3), 'v')
|
||||
local r2 = Range.new(Pos.new(nil, 0, 2), Pos.new(nil, 0, 4), 'v')
|
||||
local r3 = Range.new(Pos.new(nil, 0, 0), Pos.new(nil, 0, 5), 'v')
|
||||
local smallest = Range.smallest { r1, r2, r3 }
|
||||
assert.are.same(smallest.start, Pos.new(nil, 0, 1))
|
||||
assert.are.same(smallest.stop, Pos.new(nil, 0, 3))
|
||||
end)
|
||||
|
||||
it('clone', function()
|
||||
withbuf({ 'line one', 'and line two' }, function()
|
||||
local original = Range.from_lines(nil, 0, 1)
|
||||
local cloned = original:clone()
|
||||
assert.are.same(original.start, cloned.start)
|
||||
assert.are.same(original.stop, cloned.stop)
|
||||
assert.are.same(original.mode, cloned.mode)
|
||||
end)
|
||||
end)
|
||||
|
||||
it('line_count', function()
|
||||
withbuf({ 'line one', 'and line two', 'line three' }, function()
|
||||
local range = Range.from_lines(nil, 0, 2)
|
||||
assert.are.same(range:line_count(), 3)
|
||||
end)
|
||||
end)
|
||||
|
||||
it('to_linewise()', function()
|
||||
withbuf({ 'line one', 'and line two' }, function()
|
||||
local range = Range.new(Pos.new(nil, 0, 1), Pos.new(nil, 1, 3), 'v')
|
||||
local linewise_range = range:to_linewise()
|
||||
assert.are.same(linewise_range.start.col, 0)
|
||||
assert.are.same(linewise_range.stop.col, Pos.MAX_COL)
|
||||
assert.are.same(linewise_range.mode, 'V')
|
||||
end)
|
||||
end)
|
||||
|
||||
it('is_empty', function()
|
||||
withbuf({ 'line one', 'and line two' }, function()
|
||||
local range = Range.new(Pos.new(nil, 0, 0), Pos.new(nil, 0, 0), 'v')
|
||||
assert.is_true(range:is_empty())
|
||||
|
||||
local range2 = Range.new(Pos.new(nil, 0, 0), Pos.new(nil, 0, 1), 'v')
|
||||
assert.is_false(range2:is_empty())
|
||||
end)
|
||||
end)
|
||||
|
||||
it('trim_start', function()
|
||||
withbuf({ ' line one', 'line two' }, function()
|
||||
local range = Range.new(Pos.new(nil, 0, 0), Pos.new(nil, 0, 9), 'v')
|
||||
local trimmed = range:trim_start()
|
||||
assert.are.same(trimmed.start, Pos.new(nil, 0, 3)) -- should be after the spaces
|
||||
end)
|
||||
end)
|
||||
|
||||
it('trim_stop', function()
|
||||
withbuf({ 'line one ', 'line two' }, function()
|
||||
local range = Range.new(Pos.new(nil, 0, 0), Pos.new(nil, 0, 9), 'v')
|
||||
local trimmed = range:trim_stop()
|
||||
assert.are.same(trimmed.stop, Pos.new(nil, 0, 7)) -- should be before the spaces
|
||||
end)
|
||||
end)
|
||||
|
||||
it('contains', function()
|
||||
withbuf({ 'line one', 'and line two' }, function()
|
||||
local range = Range.new(Pos.new(nil, 0, 1), Pos.new(nil, 0, 3), 'v')
|
||||
local pos = Pos.new(nil, 0, 2)
|
||||
assert.is_true(range:contains(pos))
|
||||
|
||||
pos = Pos.new(nil, 0, 4) -- outside of range
|
||||
assert.is_false(range:contains(pos))
|
||||
end)
|
||||
end)
|
||||
|
||||
it('shrink', function()
|
||||
withbuf({ 'line one', 'and line two' }, function()
|
||||
local range = Range.new(Pos.new(nil, 0, 1), Pos.new(nil, 1, 3), 'v')
|
||||
local shrunk = range:shrink(1)
|
||||
assert.are.same(shrunk.start, Pos.new(nil, 0, 2))
|
||||
assert.are.same(shrunk.stop, Pos.new(nil, 1, 2))
|
||||
end)
|
||||
end)
|
||||
|
||||
it('must_shrink', function()
|
||||
withbuf({ 'line one', 'and line two' }, function()
|
||||
local range = Range.new(Pos.new(nil, 0, 1), Pos.new(nil, 1, 3), 'v')
|
||||
local shrunk = range:must_shrink(1)
|
||||
assert.are.same(shrunk.start, Pos.new(nil, 0, 2))
|
||||
assert.are.same(shrunk.stop, Pos.new(nil, 1, 2))
|
||||
|
||||
assert.has.error(function() range:must_shrink(100) end, 'error in Range:must_shrink: Range:shrink() returned nil')
|
||||
end)
|
||||
end)
|
||||
|
||||
it('set_visual_selection', function()
|
||||
withbuf({ 'line one', 'and line two' }, function()
|
||||
local range = Range.from_lines(nil, 0, 1)
|
||||
range:set_visual_selection()
|
||||
|
||||
assert.are.same(Pos.from_pos 'v', Pos.new(nil, 0, 0))
|
||||
assert.are.same(Pos.from_pos '.', Pos.new(nil, 1, 11))
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
Loading…
x
Reference in New Issue
Block a user