echasnovski / mini.nvim

Library of 40+ independent Lua modules improving overall Neovim (version 0.8 and higher) experience with minimal effort
MIT License
4.82k stars 181 forks source link

Repeatable movement across modules with `;` and `,` #1077

Open heygent opened 1 month ago

heygent commented 1 month ago

Contributing guidelines

Module(s)

mini.jump, mini.ai, mini.bracketed

Description

Hi, and thank you for this incredible project, it is slowly conquering my config :)

I think it would be cool if modules that deal with movements were able to make them repeatable with , and ;. Some examples of this behaviour would be:

There is an implementation of this in https://github.com/nvim-treesitter/nvim-treesitter-textobjects/blob/master/lua/nvim-treesitter/textobjects/repeatable_move.lua It is written so that other plugins can hook into it, making their respective moves repeatable, but I feel like such a mechanism would be better suited to a project like this, as it is more general in scope and it already has many modules it can be hooked into.

echasnovski commented 1 month ago

Thanks for the suggestion!

An idea of generalizable repeatable jump is interesting and I'll take a look at implementation in 'nvim-treesitter'.

However, my first instinct is that I don't really like the idea of overriding ";" and ",". Not sure why, though. Probably because they are somewhat special and the first one is already taken by 'min.jump'. I'll think about it.

heygent commented 1 month ago

I ended up monkeypatching the mini functions on my config's side. It's really nice to use, you can use this snippet to try it out (requires nvim-treesitter-textobjects):

local ts_repeat_move = require("nvim-treesitter.textobjects.repeatable_move")

local minibracketed = require('mini.bracketed')

for key, _ in pairs(minibracketed.config) do
  local move_fn = minibracketed[key]
  minibracketed[key] = function(direction, opts)
    local function repeatable_move(inopts)
        move_fn(inopts.forward and 'forward' or 'backward', opts)
    end
    ts_repeat_move.set_last_move(repeatable_move, {
      forward = direction == 'forward' or direction == 'last',
    })
    move_fn(direction, opts)
  end
end

local miniai = require('mini.ai')
local move_cursor = miniai.move_cursor

miniai.move_cursor = function(side, ai_type, id, opts)
  local function repeatable_move_cursor(inopts)
    local new_opts = vim.tbl_extend('force', opts, {
      search_method = inopts.forward and 'next' or 'prev'
    })
    move_cursor(side, ai_type, id, new_opts)
  end
  ts_repeat_move.set_last_move(
    repeatable_move_cursor,
    { forward = opts.search_method ~= 'prev' and opts.search_method ~= 'cover_or_prev' }
  )
  move_cursor(side, ai_type, id, opts)
end

local minijump = require('mini.jump')
local jump = minijump.jump

minijump.jump = function(target, backward, till, number)
  local function repeatable_jump(inopts)
    jump(target, not inopts.forward, till, 1)
  end
  ts_repeat_move.set_last_move(
    repeatable_jump,
    { forward = not backward }
  )
  jump(target, backward, till, number)
end

-- Repeat movement with ; and ,
-- ensure ; goes forward and , goes backward regardless of the last direction
vim.keymap.set({ "n", "x", "o" }, ";", ts_repeat_move.repeat_last_move_next)
vim.keymap.set({ "n", "x", "o" }, ",", ts_repeat_move.repeat_last_move_previous)

-- vim way: ; goes to the direction you were moving.
-- vim.keymap.set({ "n", "x", "o" }, ";", ts_repeat_move.repeat_last_move)
-- vim.keymap.set({ "n", "x", "o" }, ",", ts_repeat_move.repeat_last_move_opposite)