smart_n.lua

Branches > main > 79fbc3f > files / nvim / lua / smart_n.lua

--- License: 0BSD
--- https://grahp.dev/smart-n.nvim

--- In case you use something else :p
FT_MOTIONS = {"f", "F", "t", "T"}

SEARCH = "search"
FT = "f/t"
--- Holds the last type of search performed
--- Either SEARCH, FT, or nil
vim.b.last_n_type = nil

vim.api.nvim_create_autocmd("CmdlineLeave", {
  callback = function()
    local t = vim.fn.getcmdtype()
    if t == "/" or t == "?" then
      vim.b.last_n_type = SEARCH
    end
  end})

for _, key in ipairs(FT_MOTIONS) do
  vim.keymap.set({"n", "x", "o"}, key, function()
    vim.b.last_n_type = FT
    return key
  end, {expr = true, noremap = true})
end

local function smart_n(reverse, not_smart)
  if vim.b.last_n_type == FT then
    local cs = vim.fn.getcharsearch()
    if reverse then
      return (not_smart and ",") or cs.forward == 1 and "," or ";"
    else
      return (not_smart and ";") or cs.forward == 1 and ";" or ","
    end
  elseif vim.b.last_n_type == SEARCH then
    if reverse then
      return (not_smart and "N") or vim.v.searchforward == 1 and "N" or "n"
    else
      return (not_smart and "n") or vim.v.searchforward == 1 and "n" or "N"
    end
  end
end

--- Goes based on search direction which is dumb but suit yourself :p
local function not_smart_n(reverse)
  return smart_n(reverse, true)
end

--- Keybindings
-- vim.keymap.set({"n", "v", "o"}, "n", function() return smart_n(false) end, {expr = true, noremap = true})
-- vim.keymap.set({"n", "v", "o"}, "N", function() return smart_n(true) end, {expr = true, noremap = true})

--- In another file
-- local smart_n = require("smart_n").smart_n -- Or wherever you put it
-- vim.keymap.set({"n", "v", "o"}, "n", function() return smart_n(false) end, {expr = true, noremap = true})
-- vim.keymap.set({"n", "v", "o"}, "N", function() return smart_n(true) end, {expr = true, noremap = true})

--- As this keeps track of the last type of search you performed, this file should be loaded before you're actually moving around.
--- You can defer it from first paint though, i.e. vim.schedule(function() require("smart_n") end)

return {smart_n = smart_n,
        not_smart_n = not_smart_n}