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

Support for dot repeat

Open ecosse3 opened this issue 4 years ago • 17 comments

Hi,

I have a feature request which I actually yes pretty often because I'm addicted to repeating motions via dot. Is it possible to add dot repeat support?

For example, if I have mapped :HopChar2 to f key and I press motion like: dfvi (delete find vi) it shows me the first occurrence in line as s. I click "s" and everything to "vi" is deleted as expected. Then I would like to go to next line where there is some word starting on "vi" and just delete find vi again via simply clicking dot.

Thanks!

ecosse3 avatar Apr 01 '21 22:04 ecosse3

I also want to repeat the motions. However, I don't think it should be a dot-repeat. I'd rather like commands/functions for repeating the last HopChar1 and HopChar2.

ouuan avatar May 23 '21 04:05 ouuan

Sorry, I didn't notice that you were using Hop with operators. In that case it would be good to be dot-repeatable.

ouuan avatar May 23 '21 14:05 ouuan

I have honestly no idea what it would mean. If it means depending on another plugin, why not, but I haven’t planned anything yet for this.

hadronized avatar Jun 01 '21 20:06 hadronized

Going through this again. If you could scratch a scenario / an image showing exactly what kind of feature you had on your mind, that would be great. Something like what @ouuan described seems useful (repeating the last Hop command, not motion).

hadronized avatar Nov 05 '21 19:11 hadronized

Actually in love with this plugin, I found it more comfortable than easymotion, ligthspeed, and other competitors.

Thanks you very much.

Anyway, my trouble is that I cannot repeat actions.

example:

import useErrorHandling from '@hooks/useErrorHandling';
import useIsInRole from '../../../../hooks/useIsInRole';
import useIsReadOnly from '../../../../hooks/useIsReadOnly';

I want to make 2nd line and 3rd line same as 1st liine (using @import instead of relative imports) I would go to second line, place my cursor after ', then I would do

cth@<ESC>

but then, at 3rd line I cannot repeat this, even if it's the only match. it goes in some weird infinite state that It's hard to exit. I can only do CTRL-C.

video:

https://user-images.githubusercontent.com/6294464/150967887-f9b788eb-19d1-46da-9d0b-19894b683dd9.mp4

lucax88x avatar Jan 25 '22 11:01 lucax88x

That's even a simpler scenario, just searching the first h and repeating it.

https://user-images.githubusercontent.com/6294464/150967942-1fc66df6-c9e0-460d-9ab1-271b371937c3.mp4

lucax88x avatar Jan 25 '22 11:01 lucax88x

Oh man, I came here to say this, i'd LOVE to repeat previous df/ motion (as example). @phaazon any more updates on this being a possibility, or a "no, will not do" is sufficient at this point. thankms so much for this great plugin!

megalithic avatar Mar 03 '22 14:03 megalithic

👋 Just bumping this @phaazon to see if you had/have any interest or thoughts on implementation of dot repeat support with hop?

megalithic avatar May 19 '22 13:05 megalithic

Bump. I am in love with this plugin and I am suffering without ability to dot-repeat last hop-function

Strongleong avatar Jun 26 '22 23:06 Strongleong

Another bump. The plugin is great, in my opinion the only major thing it's missing at this point is the ability to dot-repeat hop jumps (hops?). I really hope it gets implemented.

iroedius avatar Jul 05 '22 21:07 iroedius

A little update what I found. Turns out that's there IS dot-repeat, but it is done a little weird. I have bind hint_char1 to f and t (inclusive and exclusive respectively), and if I prees fsa (go to first a inclusive) and press ., it will repeat only f. I mean that you need to again specify letter you want to go

Strongleong avatar Jul 05 '22 21:07 Strongleong

can tpope/vim-repeat be considered?

---updates--- actually i made a repeatable approach for hop.hint_with using tpope/vim-repeat before comment. although i played happily with it so far, i still wanna this feature can be provided by the upstream itself.

---updates: i really dont want to polute this issue--- here is how i use vim-repeat with hop.nvim (in a very limited way).

snippet
local function repeatable_hop(chars)
  assert(chars ~= nil)
  last_chars = chars
  hop.hint_with(builtin_targets.jump_targets_by_scanning_lines(builtin_targets.regex_by_case_searching(chars, true, {})), hop.opts)
  vim.fn["repeat#set"](":lua require'change-me-to-a-real-moudle-name'.repeats()\r")
end

M.repeats = function()
  if last_chars == nil then return end
  repeatable_hop(last_chars)
end

haolian9 avatar Jul 24 '22 20:07 haolian9

can tpope/vim-repeat be considered?

I have one. You can see how dot works with this in my previous message

Strongleong avatar Jul 24 '22 22:07 Strongleong

can tpope/vim-repeat be considered? ---updates--- actually i made a repeatable approach for hop.hint_with using tpope/vim-repeat before comment. although i played happily with it so far, i still wanna this feature can be provided by the upstream itself.

Would you mind sharing your solution? I've tried using repeat.vim but had no success...

Thanks!

lubaroli avatar Sep 11 '22 05:09 lubaroli

can tpope/vim-repeat be considered?

---updates--- actually i made a repeatable approach for hop.hint_with using tpope/vim-repeat before comment. although i played happily with it so far, i still wanna this feature can be provided by the upstream itself.

---updates: i really dont want to polute this issue--- here is how i use vim-repeat with hop.nvim (in a very limited way).

local function repeatable_hop(chars)
  assert(chars ~= nil)
  last_chars = chars
  hop.hint_with(builtin_targets.jump_targets_by_scanning_lines(builtin_targets.regex_by_case_searching(chars, true, {})), hop.opts)
  vim.fn["repeat#set"](":lua require'change-me-to-a-real-moudle-name'.repeats()\r")
end

M.repeats = function()
  if last_chars == nil then return end
  repeatable_hop(last_chars)
end

hey this is great! thanks for sharing!

@haolian9 would you mind expanding just a bit more on your example? for instance, what is invoking your repeatable_hop/1 function? thanks!

I've adapted to my config, however, am missing the piece of how to actually call repeatable_hop/1:

  local hop = require("hop")
  local jump_target = hop.jump_target
  local last_chars

  local function repeatable_hop(chars)
    assert(chars ~= nil)
    last_chars = chars
    hop.hint_with(
      jump_target.jump_targets_by_scanning_lines(jump_target.regex_by_case_searching(chars, true, {})),
      hop.opts
    )
    vim.fn["repeat#set"](":lua mega.fn.hop_repeater()\r")
  end

  mega.fn.hop_repeater = function()
    if last_chars == nil then return end
    repeatable_hop(last_chars)
  end

megalithic avatar Sep 12 '22 15:09 megalithic

@megalithic glad you liked it, i scratched a simple rewrite of hop.hint_char1. for more complicate uses, the behavior of Hop may vary between people's needs.

snippet

prerequisites:

  • install vim-repeat
  • put the following snippet in a proper location.
  • there are several fixme
local M = {}

local hop = require("hop")
local builtin_targets = require("hop.jump_target")

local last_chars = nil

---@param chars string
local function repeatable_hop(chars)
  assert(chars ~= nil)
  last_chars = chars
  hop.hint_with(builtin_targets.jump_targets_by_scanning_lines(builtin_targets.regex_by_case_searching(chars, true, {})), hop.opts)
  -- fixme: change to a real module
  vim.fn["repeat#set"](":lua require'my.local.hop'.repeats()\r")
end

M.repeats = function()
  if last_chars == nil then return end
  repeatable_hop(last_chars)
end

M.hint_char1 = function()
  -- a rewrite of hop.hint_char1

  local char
  while true do
    vim.api.nvim_echo({ { "hop 1 char:", "Search" } }, false, {})
    local code = vim.fn.getchar()
    -- fixme: custom char range by needs
    if code >= 61 and code <= 0x7a then
      -- [a-z]
      char = string.char(code)
      break
    elseif code == 0x20 or code == 0x1b then
      -- press space, esc to cancel
      char = nil
      break
    end
  end
  if not char then return end

  repeatable_hop(char)
end

M.setup = function()
  hop.setup({})

  -- fixme: change to a real module
  vim.api.nvim_set_keymap("n", [[f]], [[<cmd>lua require'my.local.hop'.hint_char1()<cr>]], { noremap = true })
end

return M

haolian9 avatar Sep 12 '22 22:09 haolian9

here's that same script without the need for vim-repeat (I haven't tested it thoroughly, though)

-- https://gist.github.com/kylechui/a5c1258cd2d86755f97b10fc921315c3
-- https://www.vikasraj.dev/blog/vim-dot-repeat
local hop = require("hop")
local builtin_targets = require("hop.jump_target")

_G._repeated_hop_state = {
  last_chars = nil,
  count = 0,
}

_G._repeatable_hop = function ()
  for i=1,_G._repeated_hop_state.count  do
    hop.hint_with(builtin_targets.jump_targets_by_scanning_lines(builtin_targets.regex_by_case_searching(
      _G._repeated_hop_state.last_chars, true, {})), 
    hop.opts)
  end
end

hop.setup({})
vim.api.nvim_set_keymap("n", [[f]], 
function()

  local char
  while true do
    vim.api.nvim_echo({ { "hop 1 char:", "Search" } }, false, {})
    local code = vim.fn.getchar()
    -- fixme: custom char range by needs
    if code >= 61 and code <= 0x7a then
      -- [a-z]
      char = string.char(code)
      break
    elseif code == 0x20 or code == 0x1b then
      -- press space, esc to cancel
      char = nil
      break
    end
  end
  if not char then return end

  -- setup the state to pickup in _G._repeatable_hop
  _G._repeated_hop_state = {
    last_chars = char,
    count = (vim.v.count or 0) + 1
  }

  vim.go.operatorfunc = "v:lua._repeatable_hop"
  -- return this↓ to run that↑
  return "g@l" -- see expr=true
end , { noremap = true, 
-- ↓ see "g@l"
expr = true})

this whole code can go anywhere, your init.lua or a separate lua source file

FelipeLema avatar Dec 06 '22 20:12 FelipeLema