Skip to content

Commit 9087880

Browse files
committed
fix(operators): make all operators work with empty textobject/motion
Details: - Nothing is done for built-in operators (like `y` or `d`) in case textobject/motion mapping does literally nothing. Similar is reasonable to expect from 'mini.operators' operators. Resolve #2165
1 parent cfbf58b commit 9087880

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

lua/mini/operators.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ MiniOperators.evaluate = function(mode)
331331

332332
local evaluate_func = H.get_config().evaluate.func or MiniOperators.default_evaluate_func
333333
local data = H.get_region_data(mode)
334+
if data == nil then return end
334335
data.reindent_linewise = true
335336
H.apply_content_func(evaluate_func, data)
336337
end
@@ -365,12 +366,14 @@ MiniOperators.exchange = function(mode)
365366
if not H.exchange_has_step_one() then
366367
-- Store data about first region
367368
H.cache.exchange.step_one = H.exchange_set_region_extmark(mode, true)
369+
if H.cache.exchange.step_one == nil then return end
368370

369371
-- Temporarily remap `<C-c>` to stop the exchange
370372
H.exchange_set_stop_mapping()
371373
else
372374
-- Store data about second region
373375
H.cache.exchange.step_two = H.exchange_set_region_extmark(mode, false)
376+
if H.cache.exchange.step_two == nil then return end
374377

375378
-- Do exchange
376379
H.exchange_do()
@@ -414,6 +417,7 @@ MiniOperators.multiply = function(mode)
414417

415418
local count = mode == 'visual' and vim.v.count1 or H.cache.multiply.count
416419
local data = H.get_region_data(mode)
420+
if data == nil then return end
417421
local mark_from, mark_to, submode = data.mark_from, data.mark_to, data.submode
418422

419423
H.with_temp_context({ registers = { 'x', '"' } }, function()
@@ -475,6 +479,7 @@ MiniOperators.replace = function(mode)
475479
local count = mode == 'visual' and vim.v.count1 or H.cache.replace.count
476480
local register = mode == 'visual' and vim.v.register or H.cache.replace.register
477481
local data = H.get_region_data(mode)
482+
if data == nil then return '' end
478483
data.count = count
479484
data.register = register
480485
data.reindent_linewise = H.get_config().replace.reindent_linewise
@@ -854,6 +859,7 @@ H.exchange_set_region_extmark = function(mode, add_highlight)
854859

855860
-- Compute regular marks for target region
856861
local region_data = H.get_region_data(mode)
862+
if region_data == nil then return end
857863
local submode = region_data.submode
858864
local markcoords_from, markcoords_to = H.get_mark(region_data.mark_from), H.get_mark(region_data.mark_to)
859865

@@ -1111,6 +1117,7 @@ end
11111117

11121118
-- General --------------------------------------------------------------------
11131119
H.apply_content_func = function(content_func, data)
1120+
if data == nil then return end
11141121
local mark_from, mark_to, submode = data.mark_from, data.mark_to, data.submode
11151122
local reindent_linewise = data.reindent_linewise
11161123

@@ -1186,6 +1193,11 @@ H.get_region_data = function(mode)
11861193
local mark_from = selection_is_visual and '<' or '['
11871194
local mark_to = selection_is_visual and '>' or ']'
11881195

1196+
-- Detect empty region. NOTE: This doesn't work when cursor is on first line
1197+
-- and first column, but there doesn't seem to be a better way to do that.
1198+
local pos_from, pos_to = H.get_mark(mark_from), H.get_mark(mark_to)
1199+
if pos_to[1] < pos_from[1] or (pos_to[1] == pos_from[1] and pos_to[2] < pos_from[2]) then return end
1200+
11891201
return { mode = mode, submode = submode, mark_from = mark_from, mark_to = mark_to }
11901202
end
11911203

tests/test_operators.lua

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,15 @@ T['Evaluate']['works in Normal mode for line'] = function()
467467
validate_edit({ '1 + 1', '1 + 2' }, { 1, 0 }, { 'g==', 'j', '.' }, { '2', '3' }, { 2, 0 })
468468
end
469469

470+
T['Evaluate']['works with empty textobject/motion'] = function()
471+
child.api.nvim_set_keymap('o', 'w', '<Cmd><CR>', {})
472+
child.lua('_G.x = 1')
473+
set_lines({ 'xxx' })
474+
set_cursor(1, 1)
475+
type_keys('g=', 'w')
476+
eq(get_lines(), { 'xxx' })
477+
end
478+
470479
T['Evaluate']['works in Visual mode'] = function()
471480
-- Charwise
472481
validate_edit({ '1 + 1 = (1 + 1)' }, { 1, 8 }, { 'va)', 'g=' }, { '1 + 1 = 2' }, { 1, 8 })
@@ -805,6 +814,31 @@ T['Exchange']['works with `[count]` in Normal mode for line'] = function()
805814
)
806815
end
807816

817+
T['Exchange']['works with empty textobject/motion'] = function()
818+
child.api.nvim_set_keymap('o', 'w', '<Cmd><CR>', {})
819+
820+
-- On step one
821+
set_lines({ 'xxx', 'yyy' })
822+
set_cursor(1, 1)
823+
type_keys('gx', 'w')
824+
eq(child.fn.maparg('<C-c>'), '')
825+
-- - Should ignore previous time and treat this as step one
826+
type_keys('j', 'gx', '$')
827+
eq(get_lines(), { 'xxx', 'yyy' })
828+
type_keys('k', 'gx', '$')
829+
eq(get_lines(), { 'xyy', 'yxx' })
830+
831+
-- On step two
832+
set_lines({ 'xxx', 'yyy' })
833+
set_cursor(1, 1)
834+
type_keys('gx', 'iw')
835+
-- - Should ignore this one while still allowing proper step two
836+
type_keys('jl', 'gx', 'w')
837+
eq(get_lines(), { 'xxx', 'yyy' })
838+
type_keys('gx', '$')
839+
eq(get_lines(), { 'yy', 'yxxx' })
840+
end
841+
808842
T['Exchange']['works in Visual mode'] = function()
809843
-- Charwise from - Charwise to
810844
validate_edit1d('aa bb', 0, { 'viwgx', 'w', 'viwgx' }, 'bb aa', 3)
@@ -1366,6 +1400,14 @@ T['Multiply']['works with `cmdheight=0`'] = function()
13661400
child.expect_screenshot({ redraw = false })
13671401
end
13681402

1403+
T['Multiply']['works with empty textobject/motion'] = function()
1404+
child.api.nvim_set_keymap('o', 'w', '<Cmd><CR>', {})
1405+
set_lines({ 'xxx' })
1406+
set_cursor(1, 1)
1407+
type_keys('gm', 'w')
1408+
eq(get_lines(), { 'xxx' })
1409+
end
1410+
13691411
T['Multiply']['works in Visual mode'] = function()
13701412
validate_edit1d('aa bb', 0, { 'viw', 'gm' }, 'aaaa bb', 2)
13711413

@@ -1786,6 +1828,15 @@ T['Replace']['works with `[count]` in Normal mode for line'] = function()
17861828
)
17871829
end
17881830

1831+
T['Replace']['works with empty textobject/motion'] = function()
1832+
child.api.nvim_set_keymap('o', 'w', '<Cmd><CR>', {})
1833+
set_lines({ 'xxx', 'yyy' })
1834+
set_cursor(1, 1)
1835+
type_keys('yiw', 'jl')
1836+
type_keys('gr', 'w')
1837+
eq(get_lines(), { 'xxx', 'yyy' })
1838+
end
1839+
17891840
T['Replace']['works in Visual mode'] = function()
17901841
-- Charwise selection
17911842
validate_edit({ 'aa bb' }, { 1, 0 }, { 'yiw', 'w', 'viw', 'gr' }, { 'aa aa' }, { 1, 3 })
@@ -2184,6 +2235,14 @@ T['Sort']['works in Normal mode for line'] = function()
21842235
validate_edit({ 't, r, s', 'c, a, b' }, { 1, 0 }, { 'gss', 'j', '.' }, { 'r, s, t', 'a, b, c' }, { 2, 0 })
21852236
end
21862237

2238+
T['Sort']['works with empty textobject/motion'] = function()
2239+
child.api.nvim_set_keymap('o', 'w', '<Cmd><CR>', {})
2240+
set_lines({ 'fedcba' })
2241+
set_cursor(1, 1)
2242+
type_keys('gs', 'w')
2243+
eq(get_lines(), { 'fedcba' })
2244+
end
2245+
21872246
T['Sort']['works in Visual mode'] = function()
21882247
-- Charwise region
21892248
validate_edit1d('c, a, b', 0, { 'v$', 'gs' }, 'a, b, c', 0)

0 commit comments

Comments
 (0)