Skip to content

Commit cad365c

Browse files
abeldekatechasnovski
authored andcommitted
fix(jump): make dot-repeat not consume character when there is no target
Resolve #2290
1 parent 0098de9 commit cad365c

2 files changed

Lines changed: 39 additions & 22 deletions

File tree

lua/mini/jump.lua

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ MiniJump.state = {
181181
MiniJump.jump = function(target, backward, till, n_times)
182182
if H.is_disabled() then return end
183183

184+
-- Ensure to undo "consuming a character" effect if there is no target found
185+
-- Do it here to act on dot-repeat
186+
local has_changed_cursor = false
187+
local undo_no_move = function()
188+
if not has_changed_cursor then vim.cmd('undo!') end
189+
end
190+
if MiniJump._is_expr then vim.schedule(undo_no_move) end
191+
184192
-- Dot-repeat should not change the state, so save it to later restore
185193
local is_dot_repeat = MiniJump._is_expr and not MiniJump._is_expr_init
186194
MiniJump._is_expr, MiniJump._is_expr_init = nil, nil
@@ -234,7 +242,7 @@ MiniJump.jump = function(target, backward, till, n_times)
234242

235243
-- Track cursor position to account for movement not caught by `CursorMoved`
236244
H.cache.latest_cursor = H.get_cursor_data()
237-
H.cache.has_changed_cursor = not vim.deep_equal(H.cache.latest_cursor, init_cursor_data)
245+
has_changed_cursor = not vim.deep_equal(H.cache.latest_cursor, init_cursor_data)
238246

239247
-- Restore the state if needed
240248
if is_dot_repeat then
@@ -404,11 +412,6 @@ H.make_expr_jump = function(backward, till)
404412
if isnt_repeat_jump and target == nil then return '<Esc>' end
405413
H.update_state(target)
406414

407-
vim.schedule(function()
408-
if H.cache.has_changed_cursor then return end
409-
vim.cmd('undo!')
410-
end)
411-
412415
-- Set a flag to distinguish first call from dot-repeat
413416
MiniJump._is_expr_init = true
414417

tests/test_jump.lua

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -530,37 +530,50 @@ T['Jumping with f/t/F/T']['enters jumping mode even if first jump is impossible'
530530
end
531531

532532
T['Jumping with f/t/F/T']['does nothing if there is no place to jump'] = function()
533-
-- Normal mode
534-
local validate_normal = function(keys, start_col, ref_mode)
535-
set_lines({ 'abcdefg' })
533+
local validate_single = function(keys, start_line, start_col, ref_mode)
534+
set_lines({ start_line })
536535
set_cursor(1, start_col)
537536

538537
type_keys(keys, 'd')
539538

540539
-- It shouldn't move anywhere and should not modify text
541540
eq(get_cursor(), { 1, start_col })
542-
eq(get_lines(), { 'abcdefg' })
541+
eq(get_lines(), { start_line })
542+
eq(child.fn.mode(), ref_mode)
543+
544+
-- The above applies to subsequent dot-repeats as well
545+
type_keys('.')
546+
eq(get_cursor(), { 1, start_col })
547+
eq(get_lines(), { start_line })
543548
eq(child.fn.mode(), ref_mode)
544549

545550
-- Ensure there is no jumping
546551
child.lua('MiniJump.stop_jumping()')
547552
child.ensure_normal_mode()
548553
end
549554

550-
validate_normal('f', 4, 'n')
551-
validate_normal('t', 4, 'n')
552-
validate_normal('F', 2, 'n')
553-
validate_normal('T', 2, 'n')
555+
local validate = function(line)
556+
validate_single('f', line, 4, 'n')
557+
validate_single('t', line, 4, 'n')
558+
validate_single('F', line, 2, 'n')
559+
validate_single('T', line, 2, 'n')
560+
561+
validate_single('vf', line, 4, 'v')
562+
validate_single('vt', line, 4, 'v')
563+
validate_single('vF', line, 2, 'v')
564+
validate_single('vT', line, 2, 'v')
565+
566+
validate_single('df', line, 4, 'n')
567+
validate_single('dt', line, 4, 'n')
568+
validate_single('dF', line, 2, 'n')
569+
validate_single('dT', line, 2, 'n')
570+
end
554571

555-
validate_normal('vf', 4, 'v')
556-
validate_normal('vt', 4, 'v')
557-
validate_normal('vF', 2, 'v')
558-
validate_normal('vT', 2, 'v')
572+
-- Target is present but not reachable
573+
validate('abcdefg')
559574

560-
validate_normal('df', 4, 'n')
561-
validate_normal('dt', 4, 'n')
562-
validate_normal('dF', 2, 'n')
563-
validate_normal('dT', 2, 'n')
575+
-- Target is not present
576+
validate('abcxefg')
564577
end
565578

566579
T['Jumping with f/t/F/T']['can be dot-repeated if did not jump at first'] = function()
@@ -720,6 +733,7 @@ T['Jumping with f/t/F/T']['stops jumping if no target is found'] = function()
720733
-- General idea: there was a bug which didn't reset jumping state if target
721734
-- was not found by `vim.fn.search()`. In that case, next typing of jumping
722735
-- key wouldn't make effect, but it should.
736+
-- Related test case: 'Enters jumping mode even if first jump is impossible'
723737
for _, key in ipairs({ 'f', 't', 'F', 'T' }) do
724738
local start_col = key == key:lower() and 0 or 3
725739
set_cursor(1, start_col)

0 commit comments

Comments
 (0)