substitute.nvim icon indicating copy to clipboard operation
substitute.nvim copied to clipboard

Custom action is not dot repeatable

Open pgzzdiab opened this issue 1 year ago • 4 comments

I'm running with neovim:

➜  manifest git:(main) ✗ nvim --version
NVIM v0.10.0-dev-679+gb60a2ab4c
Build type: RelWithDebInfo
LuaJIT 2.1.0-beta3

on ubuntu 22

May be something wrong in my config, I try to map the action with the following modifier (that a great feature)

require('substitute').setup{}

vim.keymap.set("n", "o", function ()
 require('substitute').operator({modifiers = {'reindent'}})
end, { noremap = true })

The action work with the mapping but it is not dot repeatable.

When I simply make the mapping

vim.keymap.set("n", "o", require('substitute').operator, { noremap = true })

the action is dot repeatable

pgzzdiab avatar Mar 17 '24 17:03 pgzzdiab

When I run noevim with -N20 I can copy paste here the log associated with the dot action after performing a replace of a work in first place. myVim.log

pgzzdiab avatar Mar 17 '24 20:03 pgzzdiab

Interesting, I'll try do do something soon

gbprod avatar Mar 22 '24 09:03 gbprod

@gbprod also having same issue. But looking at it i am not sure how would that even be possible. The indent operator motion basically overrides the inital motion triggered for the substitute. I am scratching my head on a way to fix it, but can not think of a solution to it. The most obvious way is to modify the buffer linewise, capture the motion's lines/range, and then modify lines within that block and indent it but that is a no go, slow and bad idea all around. The idea is to still be able to use something like that vim.cmd("silent keepjumps normal! '[=']") and be able to retain the original motion which was invoked on g@

asmodeus812 avatar Jul 16 '24 16:07 asmodeus812

Indent the register content before substituting should work.

(some code copy from mini.operators)

substitute.nvim git:(main) ✗ git --no-pager diff  HEAD                                  0 [19:56:39]
diff --git a/lua/substitute.lua b/lua/substitute.lua
index e5aa76a..466b3dc 100644
--- a/lua/substitute.lua
+++ b/lua/substitute.lua
@@ -34,6 +34,37 @@ function substitute.operator(options)
   vim.api.nvim_feedkeys("g@" .. (options.motion or ""), "ni", false)
 end
 
+local H = {}
+H.get_region_indent = function(mark_from, mark_to)
+  local l_from, l_to = H.get_mark(mark_from)[1], H.get_mark(mark_to)[1]
+  local lines = vim.api.nvim_buf_get_lines(0, l_from - 1, l_to, true)
+  return H.compute_indent(lines)
+end
+H.update_indent = function(lines, new_indent)
+  -- Replace current indent with new indent without affecting blank lines
+  local n_cur_indent = H.compute_indent(lines):len()
+  return vim.tbl_map(function(l)
+    if l:find("^%s*$") ~= nil then
+      return l
+    end
+    return new_indent .. l:sub(n_cur_indent + 1)
+  end, lines)
+end
+H.compute_indent = function(lines)
+  local res_indent, res_indent_width = nil, math.huge
+  local blank_indent, blank_indent_width = nil, math.huge
+  for _, l in ipairs(lines) do
+    local cur_indent = l:match("^%s*")
+    local cur_indent_width = cur_indent:len()
+    local is_blank = cur_indent_width == l:len()
+    if not is_blank and cur_indent_width < res_indent_width then
+      res_indent, res_indent_width = cur_indent, cur_indent_width
+    elseif is_blank and cur_indent_width < blank_indent_width then
+      blank_indent, blank_indent_width = cur_indent, cur_indent_width
+    end
+  end
+  return res_indent or blank_indent or ""
+end
 function substitute.operator_callback(vmode)
   local marks = utils.get_marks(0, vmode)
   substitute.state.vmode = vmode
@@ -45,7 +76,10 @@ function substitute.operator_callback(vmode)
     local regcontents = vim.fn.getreg(state.register)
     local regtype = vim.fn.getregtype(state.register)
     local replacement = vim.split(regcontents:rep(substitute.state.count):gsub("\n$", ""), "\n")
-
+    if state.vmode == "line" then
+      local init_indent = vim.api.nvim_get_current_line():match("^(%s*)")
+      replacement = H.update_indent(replacement, init_indent)
+    end
     local subs_marks = utils.substitute_text(0, marks.start, marks.finish, vmode, replacement, regt
ype)
 
     vim.api.nvim_buf_set_mark(0, "[", subs_marks[1].start.row, subs_marks[1].start.col, {})

xzbdmw avatar Jan 24 '25 05:01 xzbdmw