Skip to content

Commit

Permalink
repeatable movement
Browse files Browse the repository at this point in the history
  • Loading branch information
kiyoon committed Jan 13, 2023
1 parent a8c86f4 commit 6d02c46
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 12 deletions.
14 changes: 7 additions & 7 deletions lua/nvim-treesitter/textobjects/attach.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ local parsers = require "nvim-treesitter.parsers"
local queries = require "nvim-treesitter.query"
local M = {}

local function make_repeatable(fn)
local function make_dot_repeatable(fn)
return function()
_G._nvim_treesitter_textobject_last_function = fn
vim.o.opfunc = "v:lua._nvim_treesitter_textobject_last_function"
vim.api.nvim_feedkeys("g@l", "n", false)
end
end

function M.make_attach(normal_mode_functions, submodule, keymap_modes, opts)
function M.make_attach(functions, submodule, keymap_modes, opts)
keymap_modes = keymap_modes or "n"
opts = opts or {}
return function(bufnr, lang)
Expand All @@ -22,7 +22,7 @@ function M.make_attach(normal_mode_functions, submodule, keymap_modes, opts)

local config = configs.get_module("textobjects." .. submodule)

for _, function_call in pairs(normal_mode_functions) do
for _, function_call in pairs(functions) do
local function_description = function_call:gsub("_", " "):gsub("^%l", string.upper)
for mapping, query_metadata in pairs(config[function_call] or {}) do
local mapping_description, query
Expand All @@ -38,8 +38,8 @@ function M.make_attach(normal_mode_functions, submodule, keymap_modes, opts)
local fn = function()
require("nvim-treesitter.textobjects." .. submodule)[function_call](query)
end
if opts.repeatable then
fn = make_repeatable(fn)
if opts.dot_repeatable then
fn = make_dot_repeatable(fn)
end
vim.keymap.set(
keymap_modes,
Expand All @@ -52,7 +52,7 @@ function M.make_attach(normal_mode_functions, submodule, keymap_modes, opts)
end
end

function M.make_detach(normal_mode_functions, submodule, keymap_modes)
function M.make_detach(functions, submodule, keymap_modes)
keymap_modes = keymap_modes or "n"
return function(bufnr)
local config = configs.get_module("textobjects." .. submodule)
Expand All @@ -66,7 +66,7 @@ function M.make_detach(normal_mode_functions, submodule, keymap_modes)
vim.keymap.del({ "o", "x" }, mapping, { buffer = bufnr })
end
end
for _, function_call in pairs(normal_mode_functions) do
for _, function_call in pairs(functions) do
for mapping, query in pairs(config[function_call] or {}) do
if type(query) == "table" then
query = query[lang]
Expand Down
177 changes: 173 additions & 4 deletions lua/nvim-treesitter/textobjects/move.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ local configs = require "nvim-treesitter.configs"

local M = {}

M.last_move = nil
-- { func = move, args = { ... } }
-- { func = builtin_find, args = { ... } }

local function move(query_strings, forward, start, winid)
query_strings = shared.make_query_strings_table(query_strings)
winid = winid or vim.api.nvim_get_current_win()
Expand Down Expand Up @@ -70,16 +74,157 @@ local function move(query_strings, forward, start, winid)
end

M.goto_next_start = function(query_strings)
move(query_strings, "forward", "start")
local args = { query_strings, "forward", "start" }
move(unpack(args))
M.last_move = { func = move, args = args }
end
M.goto_next_end = function(query_strings)
move(query_strings, "forward", not "start")
local args = { query_strings, "forward", not "start" }
move(unpack(args))
M.last_move = { func = move, args = args }
end
M.goto_previous_start = function(query_strings)
move(query_strings, not "forward", "start")
local args = { query_strings, not "forward", "start" }
move(unpack(args))
M.last_move = { func = move, args = args }
end
M.goto_previous_end = function(query_strings)
move(query_strings, not "forward", not "start")
local args = { query_strings, not "forward", not "start" }
move(unpack(args))
M.last_move = { func = move, args = args }
end

-- implements naive f, F, t, T with repeat support
local function builtin_find(forward, inclusive, char, repeating)
-- forward = true -> f, t
-- inclusive = false -> t, T
-- if repeating with till (t or T, inclusive = false) then search from the next character
-- returns nil if cancelled or char
char = char or vim.fn.nr2char(vim.fn.getchar())
repeating = repeating or false

if char == vim.fn.nr2char(27) then
-- escape
return nil
end

local line = vim.api.nvim_get_current_line()
local cursor = vim.api.nvim_win_get_cursor(0)

-- find the count-th occurrence of the char in the line
local found = nil
for i = 1, vim.v.count1 do
if forward then
if not inclusive and repeating then
cursor[2] = cursor[2] + 1
end
found = line:find(char, cursor[2] + 2, true)
else
-- reverse find from the cursor position
if not inclusive and repeating then
cursor[2] = cursor[2] - 1
end

found = line:reverse():find(char, #line - cursor[2] + 1, true)
if found then
found = #line - found + 1
end
end

if not found then
return char
end

if forward then
if not inclusive then
found = found - 1
end
else
if not inclusive then
found = found + 1
end
end

cursor[2] = found - 1
repeating = true -- after the first iteration, search from the next character if not inclusive.
end

-- move to the found position
vim.api.nvim_win_set_cursor(0, { cursor[1], cursor[2] })
return char
end

M.builtin_f = function()
local char = builtin_find("forward", "inclusive")
if builtin_find ~= nil then
M.last_move = { func = builtin_find, args = { "forward", "inclusive", char, "repeating" } }
end
end

M.builtin_F = function()
local char = builtin_find(not "forward", "inclusive")
if builtin_find ~= nil then
M.last_move = { func = builtin_find, args = { not "forward", "inclusive", char, "repeating" } }
end
end

M.builtin_t = function()
local char = builtin_find("forward", not "inclusive")
if builtin_find ~= nil then
M.last_move = { func = builtin_find, args = { "forward", not "inclusive", char, "repeating" } }
end
end

M.builtin_T = function()
local char = builtin_find(not "forward", not "inclusive")
if builtin_find ~= nil then
M.last_move = { func = builtin_find, args = { not "forward", not "inclusive", char, "repeating" } }
end
end

M.repeat_last_move = function()
if M.last_move then
M.last_move.func(unpack(M.last_move.args))
end
end

M.repeat_last_move_opposite = function()
if M.last_move then
local args = { unpack(M.last_move.args) } -- copy the table
if M.last_move.func == move then
args[2] = not args[2] -- reverse the direction
else
-- builtin_find
args[1] = not args[1] -- reverse the direction
end
M.last_move.func(unpack(args))
end
end

M.repeat_last_move_next = function()
if M.last_move then
local args = { unpack(M.last_move.args) } -- copy the table
if M.last_move.func == move then
args[2] = "forward" -- set the direction to forward
else
-- builtin_find
args[1] = "forward" -- set the direction to forward
end
M.last_move.func(unpack(args))
end
end

M.repeat_last_move_previous = function()
if M.last_move then
local args = { unpack(M.last_move.args) } -- copy the table
if M.last_move.func == move then
args[2] = not "forward" -- set the direction to backward
else
-- builtin_find
args[1] = not "forward" -- set the direction to backward
end
M.last_move.func(unpack(args))
end
end

local nxo_mode_functions = { "goto_next_start", "goto_next_end", "goto_previous_start", "goto_previous_end" }
Expand Down Expand Up @@ -116,6 +261,30 @@ M.commands = {
"-complete=custom,nvim_treesitter_textobjects#available_textobjects",
},
},
TSTextobjectRepeatLastMove = {
run = M.repeat_last_move,
},
TSTextobjectRepeatLastMoveOpposite = {
run = M.repeat_last_move_opposite,
},
TSTextobjectRepeatLastMoveNext = {
run = M.repeat_last_move_next,
},
TSTextobjectRepeatLastMovePrevious = {
run = M.repeat_last_move_previous,
},
TSTextobjectBuiltinf = {
run = M.builtin_f,
},
TSTextobjectBuiltinF = {
run = M.builtin_F,
},
TSTextobjectBuiltint = {
run = M.builtin_t,
},
TSTextobjectBuiltinT = {
run = M.builtin_T,
},
}

return M
2 changes: 1 addition & 1 deletion lua/nvim-treesitter/textobjects/swap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ end

local normal_mode_functions = { "swap_next", "swap_previous" }

M.attach = attach.make_attach(normal_mode_functions, "swap", "n", { repeatable = true })
M.attach = attach.make_attach(normal_mode_functions, "swap", "n", { dot_repeatable = true })
M.detach = attach.make_detach(normal_mode_functions, "swap", "n")

M.commands = {
Expand Down

0 comments on commit 6d02c46

Please sign in to comment.