local map = require 'std.map' describe( 'std.map', function () before_each( function () package.loaded['std.map'] = nil map = require 'std.map' end ) after_each( function () pcall( vim.keymap.del, 'n', '' ) pcall( vim.keymap.del, 'n', '' ) pcall( vim.keymap.del, 'i', 'k' ) pcall( vim.keymap.del, 'n', 'gd' ) pcall( vim.keymap.del, 'x', 'gd' ) pcall( vim.keymap.del, 'n', 'gx' ) end ) local function find_keymap( mode, lhs ) local keymaps = vim.api.nvim_get_keymap( mode ) for _, km in ipairs( keymaps ) do if km.lhsraw == lhs then return km end end return nil end local function get_keymap( mode, lhs ) return assert( find_keymap( mode, lhs ) ) end describe( 'mode aliases', function () it( 'should support "n" as alias for normal mode', function () local deleter = map.n { keys = '', command = 'echo "test"' } assert.is_function( deleter ) local mapping = get_keymap( 'n', '' ) assert.is_not_nil( mapping ) assert.equal( '', mapping.lhsraw ) end ) it( 'should support "normal" as explicit normal mode', function () local deleter = map.normal { keys = '', command = 'echo "test"' } assert.is_function( deleter ) local mapping = get_keymap( 'n', '' ) assert.is_not_nil( mapping ) assert.equal( '', mapping.lhsraw ) end ) it( 'should support "i" as alias for insert mode', function () local deleter = map.i { keys = 'k', command = 'echo "insert"' } assert.is_function( deleter ) local mapping = get_keymap( 'i', 'k' ) assert.is_not_nil( mapping ) assert.equal( 'k', mapping.lhsraw ) end ) it( 'should support "insert" as explicit insert mode', function () local deleter = map.insert { keys = 'k', command = 'echo "insert"' } assert.is_function( deleter ) local mapping = get_keymap( 'i', 'k' ) assert.is_not_nil( mapping ) assert.equal( 'k', mapping.lhsraw ) end ) it( 'should support "x" as alias for visual mode', function () local deleter = map.x { keys = '', command = 'echo "visual"' } assert.is_function( deleter ) local mapping = get_keymap( 'x', '' ) assert.is_not_nil( mapping ) assert.equal( '', mapping.lhsraw ) end ) it( 'should support "visual" as explicit visual mode', function () local deleter = map.visual { keys = '', command = 'echo "visual"' } assert.is_function( deleter ) local mapping = get_keymap( 'x', '' ) assert.is_not_nil( mapping ) assert.equal( '', mapping.lhsraw ) end ) it( 'should support "v" as alias for visual_select mode', function () local deleter = map.v { keys = '', command = 'echo "v"' } assert.is_function( deleter ) local mapping = get_keymap( 'v', '' ) assert.is_not_nil( mapping ) assert.equal( '', mapping.lhsraw ) end ) it( 'should support "s" as alias for select mode', function () local deleter = map.s { keys = '', command = 'echo "s"' } assert.is_function( deleter ) local mapping = get_keymap( 's', '' ) assert.is_not_nil( mapping ) assert.equal( '', mapping.lhsraw ) end ) it( 'should support "o" as alias for operator_pending mode', function () local deleter = map.o { keys = '', command = 'echo "o"' } assert.is_function( deleter ) local mapping = get_keymap( 'o', '' ) assert.is_not_nil( mapping ) assert.equal( '', mapping.lhsraw ) end ) it( 'should support "c" as alias for command_line mode', function () local deleter = map.c { keys = '', command = 'echo "c"' } assert.is_function( deleter ) local mapping = get_keymap( 'c', '' ) assert.is_not_nil( mapping ) assert.equal( '', mapping.lhsraw ) end ) it( 'should support "t" as alias for terminal mode', function () local deleter = map.t { keys = '', command = 'echo "t"' } assert.is_function( deleter ) local mapping = get_keymap( 't', '' ) assert.is_not_nil( mapping ) assert.equal( '', mapping.lhsraw ) end ) end ) describe( 'mode chaining', function () it( 'should chain normal and visual modes', function () local deleter = map.normal.visual { keys = 'gd', command = 'echo "chained"' } assert.is_function( deleter ) local normal_mapping = get_keymap( 'n', 'gd' ) local visual_mapping = get_keymap( 'x', 'gd' ) assert.is_not_nil( normal_mapping ) assert.is_not_nil( visual_mapping ) end ) it( 'should chain multiple modes', function () local deleter = map.n.x.v { keys = 'gx', command = 'echo "multi"' } assert.is_function( deleter ) local n_mapping = get_keymap( 'n', 'gx' ) local x_mapping = get_keymap( 'x', 'gx' ) local v_mapping = get_keymap( 'v', 'gx' ) assert.is_not_nil( n_mapping ) assert.is_not_nil( x_mapping ) assert.is_not_nil( v_mapping ) end ) it( 'should return self after adding mode for further chaining', function () local result = map.normal assert.equal( map, result ) result = map.normal.visual assert.equal( map, result ) end ) it( 'should not duplicate mode when same mode is chained multiple times', function () local m = map.normal.normal.normal local modes = m.modes local n_count = 0 for _, mode in ipairs( modes ) do if mode == 'n' then n_count = n_count + 1 end end assert.equal( 1, n_count, 'mode "n" should appear exactly once in modes array before bind' ) end ) end ) describe( 'keys handling', function () it( 'should accept keys as string', function () local deleter = map.normal { keys = '', command = 'echo "string keys"' } assert.is_function( deleter ) local mapping = get_keymap( 'n', '' ) assert.is_not_nil( mapping ) assert.equal( '', mapping.lhsraw ) end ) it( 'should accept keys as table (array of keys)', function () local deleter = map.normal { keys = { '', '' }, command = 'echo "table keys"' } assert.is_function( deleter ) local mapping1 = get_keymap( 'n', '' ) local mapping2 = get_keymap( 'n', '' ) assert.is_not_nil( mapping1 ) assert.is_not_nil( mapping2 ) assert.equal( '', mapping1.lhsraw ) assert.equal( '', mapping2.lhsraw ) end ) end ) describe( 'command handling', function () it( 'should accept command as string', function () local deleter = map.normal { keys = '', command = 'echo "string command"' } assert.is_function( deleter ) local mapping = get_keymap( 'n', '' ) assert.is_not_nil( mapping ) assert.equal( 'echo "string command"', mapping.rhs ) end ) it( 'should accept command as function', function () local called = false local function test_func() called = true end local deleter = map.normal { keys = '', command = test_func } assert.is_function( deleter ) local mapping = get_keymap( 'n', '' ) assert.is_not_nil( mapping ) assert.is_function( mapping.callback ) mapping.callback() assert.is_true( called ) end ) end ) describe( 'options handling', function () it( 'should set silent=true by default', function () local deleter = map.normal { keys = '', command = 'echo "silent"' } assert.is_function( deleter ) local mapping = get_keymap( 'n', '' ) assert.is_not_nil( mapping ) assert.is_true( mapping.silent == 1 or mapping.silent == true ) end ) it( 'should concatenate category and description into desc', function () local deleter = map.normal { keys = '', command = 'echo "desc"', category = 'TestCategory', description = 'TestDescription', } assert.is_function( deleter ) local mapping = get_keymap( 'n', '' ) assert.is_not_nil( mapping ) assert.equal( 'TestCategory: TestDescription', mapping.desc ) end ) it( 'should use description alone when category is not present', function () local deleter = map.normal { keys = '', command = 'echo "desc"', description = 'JustDescription', } assert.is_function( deleter ) local mapping = get_keymap( 'n', '' ) assert.is_not_nil( mapping ) assert.equal( 'JustDescription', mapping.desc ) end ) it( 'should support buffer option for buffer-local keymap', function () vim.api.nvim_buf_set_lines( 0, 0, -1, true, { 'buffer test content' } ) local deleter = map.normal { keys = '', command = 'echo "buffer"', buffer = 0, } assert.is_function( deleter ) local keymaps = vim.api.nvim_buf_get_keymap( 0, 'n' ) local found = false for _, km in ipairs( keymaps ) do if km.lhsraw == '' then found = true break end end assert.is_true( found ) end ) it( 'should merge options field with defaults', function () local deleter = map.normal { keys = '', command = 'echo "merged"', options = { noremap = true, }, } assert.is_function( deleter ) local mapping = get_keymap( 'n', '' ) assert.is_not_nil( mapping ) assert.is_true( mapping.noremap == 1 or mapping.noremap == true ) end ) it( 'should support remap option', function () local deleter = map.normal { keys = '', command = 'echo "remap"', remap = true, } assert.is_function( deleter ) local mapping = get_keymap( 'n', '' ) assert.is_not_nil( mapping ) end ) end ) describe( 'cleanup function', function () it( 'should return a cleanup function (deleter)', function () local deleter = map.normal { keys = '', command = 'echo "delete"' } assert.is_function( deleter ) local mapping_before = get_keymap( 'n', '' ) assert.is_not_nil( mapping_before ) deleter() local mapping_after = find_keymap( 'n', '' ) assert.is_nil( mapping_after ) end ) it( 'should delete multiple keys when keys is a table', function () local deleter = map.normal { keys = { '', '' }, command = 'echo "delete multiple"', } local mapping1_before = get_keymap( 'n', '' ) local mapping2_before = get_keymap( 'n', '' ) assert.is_not_nil( mapping1_before ) assert.is_not_nil( mapping2_before ) deleter() local mapping1_after = find_keymap( 'n', '' ) local mapping2_after = find_keymap( 'n', '' ) assert.is_nil( mapping1_after ) assert.is_nil( mapping2_after ) end ) it( 'should delete buffer-local keymaps correctly', function () vim.api.nvim_buf_set_lines( 0, 0, -1, true, { 'buffer test content' } ) local deleter = map.normal { keys = '', command = 'echo "buffer delete"', buffer = 0, } local keymaps_before = vim.api.nvim_buf_get_keymap( 0, 'n' ) local found_before = false for _, km in ipairs( keymaps_before ) do if km.lhsraw == '' then found_before = true break end end assert(found_before, 'keymap should exist before deletion') deleter() local keymaps_after = vim.api.nvim_buf_get_keymap( 0, 'n' ) local found_after = false for _, km in ipairs( keymaps_after ) do if km.lhsraw == '' then found_after = true break end end assert(not found_after, 'keymap should be deleted after calling deleter') end ) end ) describe( 'default modes', function () it( 'should use normal_visual_select_operator_pending when no modes specified', function () local deleter = map { keys = '', command = 'echo "default modes"' } assert.is_function( deleter ) local n_mapping = get_keymap( 'n', '' ) local x_mapping = get_keymap( 'x', '' ) local v_mapping = get_keymap( 'v', '' ) local o_mapping = get_keymap( 'o', '' ) assert.is_not_nil( n_mapping ) assert.is_not_nil( x_mapping ) assert.is_not_nil( v_mapping ) assert.is_not_nil( o_mapping ) end ) end ) describe( 'error handling', function () it( 'should error on unknown mode', function () assert.has_error( function () map.unknown_mode { keys = '', command = 'echo "error"' } end, 'unknown keymap mode requested: unknown_mode' ) end ) it( 'should error when map.keys is not string or table', function () local invalid = 123 ---@cast invalid any local ok, err = pcall( function () map.normal { keys = invalid, command = 'echo "error"' } end ) assert.is_false( ok ) assert.is_not_nil( vim.split( tostring( err ), '\n' )[1]:match 'map.keys: expected' ) end ) it( 'should error when map.command is not function or string', function () local ok, err = pcall( function () map.normal { keys = '', command = nil } end ) assert.is_false( ok ) assert.is_not_nil( vim.split( tostring( err ), '\n' )[1]:match 'map.command: expected' ) end ) it( 'should error when map is not a table', function () local invalid = 123 ---@cast invalid any local ok, err = pcall( function () map( invalid ) end ) assert.is_false( ok ) assert.is_not_nil( vim.split( tostring( err ), '\n' )[1]:match 'map: expected' ) end ) it( 'should error on assignment to map', function () local ok, err = pcall( function () map.foo = 'bar' end ) assert.is_false( ok ) assert.is_not_nil( tostring( err ):match 'Assignments are not supported for std.map' ) end ) end ) end )