Compare commits
10 Commits
e717c0a7d5
...
121e0c0f7a
Author | SHA1 | Date | |
---|---|---|---|
121e0c0f7a | |||
f3e352ceda | |||
6d49d1a0e4 | |||
3c948ac985 | |||
8bbb2ba9c8 | |||
03c5500966 | |||
e45d51cdf5 | |||
c2ba91893f | |||
3bf3836bd7 | |||
bdd1d949de |
2
Makefile
2
Makefile
@ -9,7 +9,7 @@ fmt:
|
|||||||
stylua .
|
stylua .
|
||||||
|
|
||||||
test: $(PLENARY_DIR)
|
test: $(PLENARY_DIR)
|
||||||
nvim -u NORC --headless -c 'set packpath+=~/.local/share/nvim/site' -c 'packadd plenary.nvim' -c "PlenaryBustedDirectory spec/"
|
NVIM_APPNAME=noplugstest nvim -u NORC --headless -c 'set packpath+=~/.local/share/nvim/site' -c 'packadd plenary.nvim' -c "PlenaryBustedDirectory spec/"
|
||||||
|
|
||||||
$(PLENARY_DIR):
|
$(PLENARY_DIR):
|
||||||
git clone https://github.com/nvim-lua/plenary.nvim/ $(PLENARY_DIR)
|
git clone https://github.com/nvim-lua/plenary.nvim/ $(PLENARY_DIR)
|
||||||
|
22
README.md
22
README.md
@ -1,8 +1,8 @@
|
|||||||
# Text Tools (TT)
|
# u.nvim
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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, `u.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
|
## Features
|
||||||
|
|
||||||
@ -16,8 +16,8 @@ This is meant to be used as a **library**, not a plugin. On its own, `text-tools
|
|||||||
Lazy:
|
Lazy:
|
||||||
```lua
|
```lua
|
||||||
-- Setting `lazy = true` ensures that the library is only loaded
|
-- Setting `lazy = true` ensures that the library is only loaded
|
||||||
-- when `require 'tt.<utility>' is called.
|
-- when `require 'u.<utility>' is called.
|
||||||
{ 'jrop/text-tools.nvim', lazy = true }
|
{ 'jrop/u.nvim', lazy = true }
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@ -31,7 +31,7 @@ I love NeoVim. I am coming to love Lua. I don't like 1-based indices; perhaps I
|
|||||||
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 'tt.range'
|
local Range = require 'u.range'
|
||||||
local start = Pos.new(0, 0, 0) -- Line 1, first column
|
local start = Pos.new(0, 0, 0) -- Line 1, first column
|
||||||
local stop = Pos.new(0, 2, 0) -- Line 3, first column
|
local stop = Pos.new(0, 2, 0) -- Line 3, first column
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ range:replace(nil)
|
|||||||
Define custom (dot-repeatable) key mappings for text objects:
|
Define custom (dot-repeatable) key mappings for text objects:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local opkeymap = require 'tt.opkeymap'
|
local opkeymap = require 'u.opkeymap'
|
||||||
|
|
||||||
-- invoke this function by typing, for example, `<leader>riw`:
|
-- invoke this function by typing, for example, `<leader>riw`:
|
||||||
-- `range` will contain the bounds of the motion `iw`.
|
-- `range` will contain the bounds of the motion `iw`.
|
||||||
@ -123,7 +123,7 @@ end)
|
|||||||
To write code with indentation, use the `CodeWriter` class:
|
To write code with indentation, use the `CodeWriter` class:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local CodeWriter = require 'tt.codewriter'
|
local CodeWriter = require 'u.codewriter'
|
||||||
local cw = CodeWriter.new()
|
local cw = CodeWriter.new()
|
||||||
cw:write('{')
|
cw:write('{')
|
||||||
cw:indent(function(innerCW)
|
cw:indent(function(innerCW)
|
||||||
@ -139,8 +139,8 @@ cw:write('}')
|
|||||||
Simply by returning a `Range` or a `Pos`, you can easily and quickly define your own text objects:
|
Simply by returning a `Range` or a `Pos`, you can easily and quickly define your own text objects:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local utils = require 'tt.utils'
|
local utils = require 'u.utils'
|
||||||
local Range = require 'tt.range'
|
local Range = require 'u.range'
|
||||||
|
|
||||||
-- Select whole file:
|
-- Select whole file:
|
||||||
utils.define_text_object('ag', function()
|
utils.define_text_object('ag', function()
|
||||||
@ -153,7 +153,7 @@ end)
|
|||||||
Access and manipulate buffers easily:
|
Access and manipulate buffers easily:
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
local Buffer = require 'tt.buffer'
|
local Buffer = require 'u.buffer'
|
||||||
local buf = Buffer.current()
|
local buf = Buffer.current()
|
||||||
buf:line_count() -- the number of lines in the current buffer
|
buf:line_count() -- the number of lines in the current buffer
|
||||||
buf:get_option '...'
|
buf:get_option '...'
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
local Range = require 'tt.range'
|
local Range = require 'u.range'
|
||||||
|
|
||||||
---@class Buffer
|
---@class Buffer
|
||||||
---@field buf number
|
---@field buf number
|
@ -1,4 +1,4 @@
|
|||||||
local Buffer = require 'tt.buffer'
|
local Buffer = require 'u.buffer'
|
||||||
|
|
||||||
---@class CodeWriter
|
---@class CodeWriter
|
||||||
---@field lines string[]
|
---@field lines string[]
|
@ -1,21 +1,21 @@
|
|||||||
local Range = require 'tt.range'
|
local Range = require 'u.range'
|
||||||
local vim_repeat = require 'tt.repeat'
|
local vim_repeat = require 'u.repeat'
|
||||||
|
|
||||||
---@type fun(range: Range): nil|(fun():any)
|
---@type fun(range: Range): nil|(fun():any)
|
||||||
local __TT__OpKeymapOpFunc_rhs = nil
|
local __U__OpKeymapOpFunc_rhs = nil
|
||||||
|
|
||||||
--- This is the global utility function used for operatorfunc
|
--- This is the global utility function used for operatorfunc
|
||||||
--- in opkeymap
|
--- in opkeymap
|
||||||
---@type nil|fun(range: Range): fun():any|nil
|
---@type nil|fun(range: Range): fun():any|nil
|
||||||
---@param ty 'line'|'char'|'block'
|
---@param ty 'line'|'char'|'block'
|
||||||
-- selene: allow(unused_variable)
|
-- selene: allow(unused_variable)
|
||||||
function __TT__OpKeymapOpFunc(ty)
|
function __U__OpKeymapOpFunc(ty)
|
||||||
if __TT__OpKeymapOpFunc_rhs ~= nil then
|
if __U__OpKeymapOpFunc_rhs ~= nil then
|
||||||
local range = Range.from_op_func(ty)
|
local range = Range.from_op_func(ty)
|
||||||
local repeat_inject = __TT__OpKeymapOpFunc_rhs(range)
|
local repeat_inject = __U__OpKeymapOpFunc_rhs(range)
|
||||||
|
|
||||||
vim_repeat.set(function()
|
vim_repeat.set(function()
|
||||||
vim.o.operatorfunc = 'v:lua.__TT__OpKeymapOpFunc'
|
vim.o.operatorfunc = 'v:lua.__U__OpKeymapOpFunc'
|
||||||
if repeat_inject ~= nil and type(repeat_inject) == 'function' then repeat_inject() end
|
if repeat_inject ~= nil and type(repeat_inject) == 'function' then repeat_inject() end
|
||||||
vim_repeat.native_repeat()
|
vim_repeat.native_repeat()
|
||||||
end)
|
end)
|
||||||
@ -34,8 +34,8 @@ end
|
|||||||
---@param opts? vim.keymap.set.Opts
|
---@param opts? vim.keymap.set.Opts
|
||||||
local function opkeymap(mode, lhs, rhs, opts)
|
local function opkeymap(mode, lhs, rhs, opts)
|
||||||
vim.keymap.set(mode, lhs, function()
|
vim.keymap.set(mode, lhs, function()
|
||||||
__TT__OpKeymapOpFunc_rhs = rhs
|
__U__OpKeymapOpFunc_rhs = rhs
|
||||||
vim.o.operatorfunc = 'v:lua.__TT__OpKeymapOpFunc'
|
vim.o.operatorfunc = 'v:lua.__U__OpKeymapOpFunc'
|
||||||
return 'g@'
|
return 'g@'
|
||||||
end, vim.tbl_extend('force', opts or {}, { expr = true }))
|
end, vim.tbl_extend('force', opts or {}, { expr = true }))
|
||||||
end
|
end
|
@ -201,17 +201,8 @@ function Pos:find_match(max_chars, invocations)
|
|||||||
local is_closer = vim.tbl_contains(closers, c)
|
local is_closer = vim.tbl_contains(closers, c)
|
||||||
if not is_opener and not is_closer then return nil end
|
if not is_opener and not is_closer then return nil end
|
||||||
|
|
||||||
---@type number
|
local i, _ = vim.iter(is_opener and openers or closers):enumerate():find(function(_, c2) return c == c2 end)
|
||||||
local i
|
local c_match = (is_opener and closers or openers)[i]
|
||||||
---@type string
|
|
||||||
local c_match
|
|
||||||
if is_opener then
|
|
||||||
i, _ = vim.iter(openers):enumerate():find(function(_, c2) return c == c2 end)
|
|
||||||
c_match = closers[i]
|
|
||||||
else
|
|
||||||
i, _ = vim.iter(closers):enumerate():find(function(_, c2) return c == c2 end)
|
|
||||||
c_match = openers[i]
|
|
||||||
end
|
|
||||||
|
|
||||||
---@type Pos|nil
|
---@type Pos|nil
|
||||||
local cur = self
|
local cur = self
|
||||||
@ -224,13 +215,7 @@ function Pos:find_match(max_chars, invocations)
|
|||||||
if max_chars < 0 then return nil end
|
if max_chars < 0 then return nil end
|
||||||
end
|
end
|
||||||
|
|
||||||
if is_opener then
|
return cur:next(is_opener and 1 or -1)
|
||||||
-- scan forward
|
|
||||||
return cur:next()
|
|
||||||
else
|
|
||||||
-- scan backward
|
|
||||||
return cur:next(-1)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- scan until we find a match:
|
-- scan until we find a match:
|
@ -1,6 +1,12 @@
|
|||||||
local Pos = require 'tt.pos'
|
local Pos = require 'u.pos'
|
||||||
local State = require 'tt.state'
|
local State = require 'u.state'
|
||||||
local utils = require 'tt.utils'
|
|
||||||
|
local orig_on_yank = vim.highlight.on_yank
|
||||||
|
local on_yank_enabled = true;
|
||||||
|
(vim.highlight --[[@as any]]).on_yank = function(opts)
|
||||||
|
if not on_yank_enabled then return end
|
||||||
|
return orig_on_yank(opts)
|
||||||
|
end
|
||||||
|
|
||||||
---@class Range
|
---@class Range
|
||||||
---@field start Pos
|
---@field start Pos
|
||||||
@ -105,6 +111,7 @@ function Range.from_text_object(text_obj, opts)
|
|||||||
local positions
|
local positions
|
||||||
vim.api.nvim_buf_call(opts.buf, function()
|
vim.api.nvim_buf_call(opts.buf, function()
|
||||||
positions = State.run(0, function(s)
|
positions = State.run(0, function(s)
|
||||||
|
s:track_winview()
|
||||||
s:track_register '"'
|
s:track_register '"'
|
||||||
s:track_pos '.'
|
s:track_pos '.'
|
||||||
s:track_pos "'["
|
s:track_pos "'["
|
||||||
@ -116,10 +123,15 @@ function Range.from_text_object(text_obj, opts)
|
|||||||
null_pos:save_to_pos "'["
|
null_pos:save_to_pos "'["
|
||||||
null_pos:save_to_pos "']"
|
null_pos:save_to_pos "']"
|
||||||
|
|
||||||
-- For some reason,
|
local prev_on_yank_enabled = on_yank_enabled
|
||||||
-- vim.cmd.normal { cmd = 'normal', args = { '""y' .. text_obj }, mods = { silent = true } }
|
on_yank_enabled = false
|
||||||
-- does not work in all cases. We resort to using feedkeys instead:
|
vim.cmd.normal {
|
||||||
utils.feedkeys('""y' .. text_obj, opts.user_defined and 'mx' or 'nx') -- 'm' = remap, 'n' = noremap
|
cmd = 'normal',
|
||||||
|
bang = not opts.user_defined,
|
||||||
|
args = { '""y' .. text_obj },
|
||||||
|
mods = { silent = true },
|
||||||
|
}
|
||||||
|
on_yank_enabled = prev_on_yank_enabled
|
||||||
|
|
||||||
local start = Pos.from_pos "'["
|
local start = Pos.from_pos "'["
|
||||||
local stop = Pos.from_pos "']"
|
local stop = Pos.from_pos "']"
|
||||||
@ -340,7 +352,6 @@ function Range:replace(replacement)
|
|||||||
-- Fixup the bounds:
|
-- Fixup the bounds:
|
||||||
local last_line = vim.api.nvim_buf_get_lines(self.stop.buf, self.stop.lnum, self.stop.lnum + 1, false)[1] or ''
|
local last_line = vim.api.nvim_buf_get_lines(self.stop.buf, self.stop.lnum, self.stop.lnum + 1, false)[1] or ''
|
||||||
local max_col = #last_line
|
local max_col = #last_line
|
||||||
if last_line ~= '' then max_col = max_col + 1 end
|
|
||||||
|
|
||||||
vim.api.nvim_buf_set_text(
|
vim.api.nvim_buf_set_text(
|
||||||
self.start.buf,
|
self.start.buf,
|
||||||
@ -403,10 +414,53 @@ function Range:set_visual_selection()
|
|||||||
self.start:save_to_mark 'a'
|
self.start:save_to_mark 'a'
|
||||||
self.stop:save_to_mark 'b'
|
self.stop:save_to_mark 'b'
|
||||||
local mode = self.mode
|
local mode = self.mode
|
||||||
if vim.api.nvim_get_mode().mode == 'n' then utils.feedkeys(mode) end
|
|
||||||
utils.feedkeys '`ao`b'
|
local normal_cmd_args = ''
|
||||||
|
if vim.api.nvim_get_mode().mode == 'n' then normal_cmd_args = normal_cmd_args .. mode end
|
||||||
|
normal_cmd_args = normal_cmd_args .. '`ao`b'
|
||||||
|
vim.cmd { cmd = 'normal', args = { normal_cmd_args }, bang = true }
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@param group string
|
||||||
|
---@param opts? { timeout?: number, priority?: number, on_macro?: boolean }
|
||||||
|
function Range:highlight(group, opts)
|
||||||
|
opts = opts or { on_macro = false }
|
||||||
|
if opts.on_macro == nil then opts.on_macro = false end
|
||||||
|
|
||||||
|
local in_macro = vim.fn.reg_executing() ~= ''
|
||||||
|
if not opts.on_macro and in_macro then return { clear = function() end } end
|
||||||
|
|
||||||
|
local ns = vim.api.nvim_create_namespace ''
|
||||||
|
State.run(self.start.buf, function(s)
|
||||||
|
if not in_macro then s:track_winview() end
|
||||||
|
|
||||||
|
vim.highlight.range(
|
||||||
|
self.start.buf,
|
||||||
|
ns,
|
||||||
|
group,
|
||||||
|
{ self.start.lnum, self.start.col },
|
||||||
|
{ self.stop.lnum, self.stop.col },
|
||||||
|
{
|
||||||
|
inclusive = true,
|
||||||
|
priority = opts.priority,
|
||||||
|
regtype = self.mode,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end)
|
||||||
|
vim.cmd.redraw()
|
||||||
|
|
||||||
|
local function clear()
|
||||||
|
vim.api.nvim_buf_clear_namespace(self.start.buf, ns, self.start.lnum, self.stop.lnum + 1)
|
||||||
|
vim.cmd.redraw()
|
||||||
|
end
|
||||||
|
if opts.timeout ~= nil then vim.defer_fn(clear, opts.timeout) end
|
||||||
|
|
||||||
|
return { ns = ns, clear = clear }
|
||||||
|
end
|
||||||
|
|
||||||
return Range
|
return Range
|
@ -1,40 +1,36 @@
|
|||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local function _normal(cmd) vim.cmd.normal { cmd = 'normal', args = { cmd }, bang = true } end
|
local function _normal(cmd) vim.cmd.normal { cmd = 'normal', args = { cmd }, bang = true } end
|
||||||
local function _feedkeys(keys, mode)
|
|
||||||
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(keys, true, false, true), mode or 'nx', true)
|
|
||||||
end
|
|
||||||
|
|
||||||
M.native_repeat = function() _feedkeys '.' end
|
M.native_repeat = function() _normal '.' end
|
||||||
M.native_undo = function() _feedkeys 'u' end
|
M.native_undo = function() _normal 'u' end
|
||||||
|
|
||||||
|
local function update_ts() vim.b.tt_changedtick = vim.b.changedtick end
|
||||||
|
|
||||||
---@param cmd? string|fun():unknown
|
---@param cmd? string|fun():unknown
|
||||||
function M.set(cmd)
|
function M.set(cmd)
|
||||||
local ts = vim.b.changedtick
|
update_ts()
|
||||||
vim.b.tt_changedtick = ts
|
|
||||||
if cmd ~= nil then vim.b.tt_repeatcmd = cmd end
|
if cmd ~= nil then vim.b.tt_repeatcmd = cmd end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function tt_was_last_repeatable()
|
||||||
|
local ts, tt_ts = vim.b.changedtick, vim.b.tt_changedtick
|
||||||
|
return tt_ts ~= nil and ts <= tt_ts
|
||||||
|
end
|
||||||
|
|
||||||
---@generic T
|
---@generic T
|
||||||
---@param cmd string|fun():T
|
---@param cmd string|fun():T
|
||||||
---@return T
|
---@return T
|
||||||
function M.run(cmd)
|
function M.run(cmd)
|
||||||
M.set(cmd)
|
M.set(cmd)
|
||||||
local result = cmd()
|
local result = cmd()
|
||||||
M.set()
|
update_ts()
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.do_repeat()
|
function M.do_repeat()
|
||||||
local ts, tt_ts, tt_cmd = vim.b.changedtick, vim.b.tt_changedtick, vim.b.tt_repeatcmd
|
local tt_cmd = vim.b.tt_repeatcmd
|
||||||
if
|
if not tt_was_last_repeatable() or (type(tt_cmd) ~= 'function' and type(tt_cmd) ~= 'string') then
|
||||||
-- (force formatter break)
|
|
||||||
tt_ts == nil
|
|
||||||
or tt_cmd == nil
|
|
||||||
-- has the buffer been modified after we last modified it?
|
|
||||||
or ts > tt_ts
|
|
||||||
or (type(tt_cmd) ~= 'function' and type(tt_cmd) ~= 'string')
|
|
||||||
then
|
|
||||||
return M.native_repeat()
|
return M.native_repeat()
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -52,10 +48,9 @@ function M.do_repeat()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function M.undo()
|
function M.undo()
|
||||||
|
local tt_was_last_repeatable_before_undo = tt_was_last_repeatable()
|
||||||
M.native_undo()
|
M.native_undo()
|
||||||
-- Update the current TS on the next event tick,
|
if tt_was_last_repeatable_before_undo then update_ts() end
|
||||||
-- to make sure we get the latest
|
|
||||||
vim.schedule(M.set)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.setup()
|
function M.setup()
|
@ -5,6 +5,7 @@
|
|||||||
---@field positions table
|
---@field positions table
|
||||||
---@field keymaps { mode: string; lhs: any, rhs: any, buffer?: number }[]
|
---@field keymaps { mode: string; lhs: any, rhs: any, buffer?: number }[]
|
||||||
---@field global_options table<string, any>
|
---@field global_options table<string, any>
|
||||||
|
---@field win_view vim.fn.winsaveview.ret|nil
|
||||||
local State = {}
|
local State = {}
|
||||||
|
|
||||||
---@param buf number
|
---@param buf number
|
||||||
@ -65,6 +66,8 @@ function State:track_pos(pos) self.positions[pos] = vim.fn.getpos(pos) end
|
|||||||
---@param nm string
|
---@param nm string
|
||||||
function State:track_global_option(nm) self.global_options[nm] = vim.g[nm] end
|
function State:track_global_option(nm) self.global_options[nm] = vim.g[nm] end
|
||||||
|
|
||||||
|
function State:track_winview() self.win_view = vim.fn.winsaveview() end
|
||||||
|
|
||||||
function State:restore()
|
function State:restore()
|
||||||
for reg, val in pairs(self.registers) do
|
for reg, val in pairs(self.registers) do
|
||||||
vim.fn.setreg(reg, val)
|
vim.fn.setreg(reg, val)
|
||||||
@ -81,6 +84,7 @@ function State:restore()
|
|||||||
for nm, val in pairs(self.global_options) do
|
for nm, val in pairs(self.global_options) do
|
||||||
vim.g[nm] = val
|
vim.g[nm] = val
|
||||||
end
|
end
|
||||||
|
if self.win_view ~= nil then vim.fn.winrestview(self.win_view) end
|
||||||
end
|
end
|
||||||
|
|
||||||
return State
|
return State
|
@ -6,13 +6,6 @@ local M = {}
|
|||||||
|
|
||||||
---@alias QfItem { col: number, filename: string, kind: string, lnum: number, text: string }
|
---@alias QfItem { col: number, filename: string, kind: string, lnum: number, text: string }
|
||||||
---@alias KeyMaps table<string, fun(): any | string> }
|
---@alias KeyMaps table<string, fun(): any | string> }
|
||||||
|
|
||||||
---@param keys string
|
|
||||||
---@param mode? string
|
|
||||||
function M.feedkeys(keys, mode)
|
|
||||||
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(keys, true, false, true), mode or 'nx', true)
|
|
||||||
end
|
|
||||||
|
|
||||||
---@alias CmdArgs { args: string; bang: boolean; count: number; fargs: string[]; line1: number; line2: number; mods: string; name: string; range: 0|1|2; reg: string; smods: any; info: Range|nil }
|
---@alias CmdArgs { args: string; bang: boolean; count: number; fargs: string[]; line1: number; line2: number; mods: string; name: string; range: 0|1|2; reg: string; smods: any; info: Range|nil }
|
||||||
|
|
||||||
--- A utility for creating user commands that also pre-computes useful information
|
--- A utility for creating user commands that also pre-computes useful information
|
||||||
@ -31,7 +24,7 @@ end
|
|||||||
---@param cmd string | fun(args: CmdArgs): any
|
---@param cmd string | fun(args: CmdArgs): any
|
||||||
---@param opts? { nargs?: 0|1|'*'|'?'|'+'; range?: boolean|'%'|number; count?: boolean|number, addr?: string; completion?: string }
|
---@param opts? { nargs?: 0|1|'*'|'?'|'+'; range?: boolean|'%'|number; count?: boolean|number, addr?: string; completion?: string }
|
||||||
function M.ucmd(name, cmd, opts)
|
function M.ucmd(name, cmd, opts)
|
||||||
local Range = require 'tt.range'
|
local Range = require 'u.range'
|
||||||
|
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local cmd2 = cmd
|
local cmd2 = cmd
|
||||||
@ -48,8 +41,8 @@ end
|
|||||||
---@param fn fun(key_seq: string):Range|Pos|nil
|
---@param fn fun(key_seq: string):Range|Pos|nil
|
||||||
---@param opts? { buffer: number|nil }
|
---@param opts? { buffer: number|nil }
|
||||||
function M.define_text_object(key_seq, fn, opts)
|
function M.define_text_object(key_seq, fn, opts)
|
||||||
local Range = require 'tt.range'
|
local Range = require 'u.range'
|
||||||
local Pos = require 'tt.pos'
|
local Pos = require 'u.pos'
|
||||||
|
|
||||||
if opts ~= nil and opts.buffer == 0 then opts.buffer = vim.api.nvim_get_current_buf() end
|
if opts ~= nil and opts.buffer == 0 then opts.buffer = vim.api.nvim_get_current_buf() end
|
||||||
|
|
||||||
@ -61,16 +54,16 @@ function M.define_text_object(key_seq, fn, opts)
|
|||||||
local range = range_or_pos --[[@as Range]]
|
local range = range_or_pos --[[@as Range]]
|
||||||
range:set_visual_selection()
|
range:set_visual_selection()
|
||||||
else
|
else
|
||||||
M.feedkeys '<Esc>'
|
vim.cmd { cmd = 'normal', args = { '<Esc>' }, bang = true }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.keymap.set({ 'x' }, key_seq, handle_visual, opts and { buffer = opts.buffer } or nil)
|
vim.keymap.set({ 'x' }, key_seq, handle_visual, opts and { buffer = opts.buffer } or nil)
|
||||||
|
|
||||||
local function handle_normal()
|
local function handle_normal()
|
||||||
local State = require 'tt.state'
|
local State = require 'u.state'
|
||||||
|
|
||||||
-- enter visual mode:
|
-- enter visual mode:
|
||||||
M.feedkeys 'v'
|
vim.cmd { cmd = 'normal', args = { 'v' }, bang = true }
|
||||||
|
|
||||||
local range_or_pos = fn(key_seq)
|
local range_or_pos = fn(key_seq)
|
||||||
if range_or_pos == nil then return end
|
if range_or_pos == nil then return end
|
||||||
@ -93,6 +86,25 @@ function M.define_text_object(key_seq, fn, opts)
|
|||||||
vim.keymap.set({ 'o' }, key_seq, handle_normal, opts and { buffer = opts.buffer } or nil)
|
vim.keymap.set({ 'o' }, key_seq, handle_normal, opts and { buffer = opts.buffer } or nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@type fun(): nil|(fun():any)
|
||||||
|
local __U__RepeatableOpFunc_rhs = nil
|
||||||
|
|
||||||
|
--- This is the global utility function used for operatorfunc
|
||||||
|
--- in repeatablemap
|
||||||
|
---@type nil|fun(range: Range): fun():any|nil
|
||||||
|
-- selene: allow(unused_variable)
|
||||||
|
function __U__RepeatableOpFunc()
|
||||||
|
if __U__RepeatableOpFunc_rhs ~= nil then __U__RepeatableOpFunc_rhs() end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.repeatablemap(mode, lhs, rhs, opts)
|
||||||
|
vim.keymap.set(mode, lhs, function()
|
||||||
|
__U__RepeatableOpFunc_rhs = rhs
|
||||||
|
vim.o.operatorfunc = 'v:lua.__U__RepeatableOpFunc'
|
||||||
|
return 'g@ '
|
||||||
|
end, vim.tbl_extend('force', opts or {}, { expr = true }))
|
||||||
|
end
|
||||||
|
|
||||||
function M.get_editor_dimensions()
|
function M.get_editor_dimensions()
|
||||||
local w = 0
|
local w = 0
|
||||||
local h = 0
|
local h = 0
|
@ -1,5 +1,5 @@
|
|||||||
local Buffer = require 'tt.buffer'
|
local Buffer = require 'u.buffer'
|
||||||
local withbuf = require '__tt_test_tools'
|
local withbuf = loadfile './spec/withbuf.lua'()
|
||||||
|
|
||||||
describe('Buffer', function()
|
describe('Buffer', function()
|
||||||
it('should replace all lines', function()
|
it('should replace all lines', function()
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
local CodeWriter = require 'tt.codewriter'
|
local CodeWriter = require 'u.codewriter'
|
||||||
|
|
||||||
describe('CodeWriter', function()
|
describe('CodeWriter', function()
|
||||||
it('should write with indentation', function()
|
it('should write with indentation', function()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
local Pos = require 'tt.pos'
|
local Pos = require 'u.pos'
|
||||||
local withbuf = require '__tt_test_tools'
|
local withbuf = loadfile './spec/withbuf.lua'()
|
||||||
|
|
||||||
describe('Pos', function()
|
describe('Pos', function()
|
||||||
it('get a char from a given position', function()
|
it('get a char from a given position', function()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
local Range = require 'tt.range'
|
local Range = require 'u.range'
|
||||||
local Pos = require 'tt.pos'
|
local Pos = require 'u.pos'
|
||||||
local withbuf = require '__tt_test_tools'
|
local withbuf = loadfile './spec/withbuf.lua'()
|
||||||
|
|
||||||
describe('Range', function()
|
describe('Range', function()
|
||||||
it('get text in buffer', function()
|
it('get text in buffer', function()
|
||||||
@ -455,4 +455,20 @@ describe('Range', function()
|
|||||||
assert.are.same(Pos.from_pos '.', Pos.new(nil, 1, 11))
|
assert.are.same(Pos.from_pos '.', Pos.new(nil, 1, 11))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('selections set to past the EOL should not error', function()
|
||||||
|
withbuf({ 'Rg SET NAMES' }, function()
|
||||||
|
local b = vim.api.nvim_get_current_buf()
|
||||||
|
local r = Range.new(Pos.new(b, 0, 3), Pos.new(b, 0, 12), 'v')
|
||||||
|
r:replace 'bleh'
|
||||||
|
assert.are.same({ 'Rg bleh' }, vim.api.nvim_buf_get_lines(b, 0, -1, false))
|
||||||
|
end)
|
||||||
|
|
||||||
|
withbuf({ 'Rg SET NAMES' }, function()
|
||||||
|
local b = vim.api.nvim_get_current_buf()
|
||||||
|
local r = Range.new(Pos.new(b, 0, 3), Pos.new(b, 0, 11), 'v')
|
||||||
|
r:replace 'bleh'
|
||||||
|
assert.are.same({ 'Rg bleh' }, vim.api.nvim_buf_get_lines(b, 0, -1, false))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user