8000 mini.nvim/lua/mini/jump.lua at main · Diomendius/mini.nvim · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
542 lines (461 loc) · 19.6 KB

jump.lua

File metadata and controls

542 lines (461 loc) · 19.6 KB
local is_repeat_jump = backward == nil or till == nil
local target = is_repeat_jump and MiniJump.state.target or H.get_target()
-- Stop if user supplied invalid target
if target == nil then return '<Esc>' end
H.update_state(target, backward, till, vim.v.count1)
vim.schedule(function()
if H.cache.has_changed_cursor then return end
vim.cmd('undo!')
end)
return 'v<Cmd>lua MiniJump.jump()<CR>'
end
end
-- Autocommands ---------------------------------------------------------------
H.on_cursormoved = function()
-- Check if jumping to avoid unnecessary actions on every CursorMoved
if MiniJump.state.jumping then
H.cache.n_cursor_moved = H.cache.n_cursor_moved + 1
-- Stop jumping only if `CursorMoved` was not a result of smart jump
if H.cache.n_cursor_moved > 1 then MiniJump.stop_jumping() end
end
end
-- Pattern matching -----------------------------------------------------------
H.make_search_data = function()
local target = vim.fn.escape(MiniJump.state.target, [[\]])
local backward, till = MiniJump.state.backward, MiniJump.state.till
local flags = backward and 'Wb' or 'W'
local pattern, hl_pattern
if till then
-- General logic: moving pattern should match just before/after target,
-- while highlight pattern should match target for every "movable" place.
-- Also allow checking for "just before/after" across lines by accepting
-- `\n` as possible match.
if backward then
-- NOTE: use `\@<=` instead of `\zs` because it behaves better in case of
-- consecutive matches (like `xxxx` for target `x`)
pattern = target .. [[\@<=\_.]]
hl_pattern = target .. [[\ze\_.]]
else
pattern = [[\_.\ze]] .. target
hl_pattern = [[\_.\@<=]] .. target
end
else
local is_visual = vim.tbl_contains({ 'v', 'V', '\22' }, vim.fn.mode())
local is_exclusive = vim.o.selection == 'exclusive'
if not backward and is_visual and is_exclusive then
-- Still select target in case of exclusive visual selection
pattern = target .. [[\zs\_.]]
hl_pattern = target .. [[\ze\_.]]
else
pattern = target
hl_pattern = target
end
end
-- Enable 'very nomagic' mode and possibly case-insensitivity
local ignore_case = vim.o.ignorecase and (not vim.o.smartcase or target == target:lower())
local prefix = ignore_case and [[\V\c]] or [[\V]]
pattern, hl_pattern = prefix .. pattern, prefix .. hl_pattern
return pattern, hl_pattern, flags
end
-- Highlighting ---------------------------------------------------------------
H.highlight = function(pattern)
-- Don't do anything if already highlighting input pattern
if H.is_highlighting(pattern) then return end
-- Stop highlighting possible previous pattern. Needed to adjust highlighting
-- when inside jumping but a different kind one. Example: first jump with
-- `till = false` and then, without jumping stop, jump to same character with
-- `till = true`. If this character is first on line, highlighting should change
H.unhighlight()
-- Never highlight in Insert mode
if vim.fn.mode() == 'i' then return end
local match_id = vim.fn.matchadd('MiniJump', pattern)
H.window_matches[vim.api.nvim_get_current_win()] = { id = match_id, pattern = pattern }
end
H.unhighlight = function()
-- Remove highlighting from all windows as jumping is intended to work only
-- in current window. This will work also from other (usually popup) window.
for win_id, match_info in pairs(H.window_matches) do
if vim.api.nvim_win_is_valid(win_id) then
-- Use `pcall` because there is an error if match id is not present. It
-- can happen if something else called `clearmatches`.
pcall(vim.fn.matchdelete, match_info.id, win_id)
H.window_matches[win_id] = nil
end
end
end
---@param pattern string|nil Highlight pattern to check for. If `nil`, checks for
--- any highlighting registered in current window.
---@private
H.is_highlighting = function(pattern)
local win_id = vim.api.nvim_get_current_win()
local match_info = H.window_matches[win_id]
if match_info == nil then return false end
return pattern == nil or match_info.pattern == pattern
end
-- Utilities ------------------------------------------------------------------
H.echo = function(msg, is_important)
if H.get_config().silent then return end
-- Construct message chunks
msg = type(msg) == 'string' and { { msg } } or msg
table.insert(msg, 1, { '(mini.jump) ', 'WarningMsg' })
-- Avoid hit-enter-prompt
local chunks = msg
if not is_important then
chunks = {}
local max_width = vim.o.columns * math.max(vim.o.cmdheight - 1, 0) + vim.v.echospace
local tot_width = 0
for _, ch in ipairs(msg) do
local new_ch = { vim.fn.strcharpart(ch[1], 0, max_width - tot_width), ch[2] }
table.insert(chunks, new_ch)
tot_width = tot_width + vim.fn.strdisplaywidth(new_ch[1])
if tot_width >= max_width then break end
end
end
-- Echo. Force redraw to ensure that it is effective (`:h echo-redraw`)
vim.cmd([[echo '' | redraw]])
vim.api.nvim_echo(chunks, is_important, {})
end
H.unecho = function()
if H.cache.msg_shown then vim.cmd([[echo '' | redraw]]) end
end
H.message = function(msg) H.echo(msg, true) end
H.update_state = function(target, backward, till, n_times)
MiniJump.state.mode = vim.fn.mode(1)
-- Don't use `? and <1> or <2>` because it doesn't work when `<1>` is `false`
if target ~= nil then MiniJump.state.target = target end
if backward ~= nil then MiniJump.state.backward = backward end
if till ~= nil then MiniJump.state.till = till end
if n_times ~= nil then MiniJump.state.n_times = n_times end
end
H.get_cursor_data = function() return { vim.api.nvim_get_current_win(), vim.api.nvim_win_get_cursor(0) } end
H.get_target = function()
local needs_help_msg = true
vim.defer_fn(function()
if not needs_help_msg then return end
H.echo('Enter target single character ')
H.cache.msg_shown = true
end, 1000)
local ok, char = pcall(vim.fn.getcharstr)
needs_help_msg = false
H.unecho()
-- Terminate if couldn't get input (like with <C-c>) or it is `<Esc>`
if not ok or char == '\27' then return end
return char
end
H.map = function(mode, lhs, rhs, opts)
if lhs == '' then return end
opts = vim.tbl_deep_extend('force', { silent = true }, opts or {})
vim.keymap.set(mode, lhs, rhs, opts)
end
return MiniJump
0