From 954b4d95c8f47d0d4c2102e14f799884957a1332 Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Mon, 1 Jan 2024 20:55:39 -0800 Subject: [PATCH] feat: partial match for multi-mode maps in duplicates (#57) fix: capture more than two duplicates of a mapping refactor: store modes as table, convert to string on render --- lua/hawtkeys/ts.lua | 4 +- lua/hawtkeys/ui.lua | 10 ++- lua/hawtkeys/utils.lua | 42 +++++++++++- tests/hawtkeys/dupes_spec.lua | 65 +++++++++++++++++++ tests/hawtkeys/example_configs/duplicates.lua | 7 ++ tests/hawtkeys/example_configs/multi_mode.lua | 5 ++ tests/hawtkeys/ts_spec.lua | 13 ++++ 7 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 tests/hawtkeys/dupes_spec.lua create mode 100644 tests/hawtkeys/example_configs/duplicates.lua create mode 100644 tests/hawtkeys/example_configs/multi_mode.lua diff --git a/lua/hawtkeys/ts.lua b/lua/hawtkeys/ts.lua index e810e95..a1a8a14 100644 --- a/lua/hawtkeys/ts.lua +++ b/lua/hawtkeys/ts.lua @@ -223,7 +223,7 @@ local function find_maps_in_file(filePath) end i = i + 1 end - map.mode = table.concat(modes, ", ") + map.mode = modes end table.insert(tsKeymaps, map) end @@ -311,7 +311,7 @@ local function find_maps_in_file(filePath) end i = i + 1 end - map.mode = table.concat(modes, ", ") + map.mode = modes end table.insert(tsKeymaps, map) end diff --git a/lua/hawtkeys/ui.lua b/lua/hawtkeys/ui.lua index 61a0333..221522b 100644 --- a/lua/hawtkeys/ui.lua +++ b/lua/hawtkeys/ui.lua @@ -265,10 +265,16 @@ M.show_all = function() local pattern = "%s (%s) - %s" for i, data in ipairs(all) do local filename = utils.reduceHome(data.from_file) - local line = pattern:format(data.lhs, data.mode, filename) + local mode + if type(data.mode) == "table" then + mode = table.concat(data.mode, ", ") + else + mode = data.mode + end + local line = pattern:format(data.lhs, mode, filename) local offset_mode = #data.lhs + 2 - local offset_file = offset_mode + #data.mode + 2 + local offset_file = offset_mode + #mode + 2 local l2 = data.rhs if l2 == nil or l2 == "" then diff --git a/lua/hawtkeys/utils.lua b/lua/hawtkeys/utils.lua index 21e826c..1143777 100644 --- a/lua/hawtkeys/utils.lua +++ b/lua/hawtkeys/utils.lua @@ -68,17 +68,55 @@ function M.merge_tables(t1, t2) return t3 end +---Returns true if a and b contain any of the same values +---@param a string | string[] +---@param b string | string[] +---@return boolean +local function match_modes(a, b) + if type(a) == "string" then + a = { a } + end + if type(b) == "string" then + b = { b } + end + -- note: there will only ever be a few modes in each table. this is fine. + for _, v in ipairs(a) do + for _, v2 in ipairs(b) do + if v == v2 then + return true + end + end + end + return false +end + ---@param keymaps HawtkeysKeyMapData[] ---@return { [string]: HawtkeysKeyMapData[] } function M.find_duplicates(keymaps) local duplicates = {} + local seen = {} for _, v in pairs(keymaps) do for _, v2 in pairs(keymaps) do - if v.lhs == v2.lhs and v.mode == v2.mode and v.rhs ~= v2.rhs then - duplicates[v.lhs] = { v, v2 } + if + v.lhs == v2.lhs + and v.rhs ~= v2.rhs + and match_modes(v.mode or "n", v2.mode or "n") + then + if not seen[v.lhs] then + seen[v.lhs] = {} + end + if duplicates[v.lhs] == nil then + duplicates[v.lhs] = { v, v2 } + seen[v.lhs][v] = true + seen[v.lhs][v2] = true + elseif seen[v.lhs] == nil or seen[v.lhs][v2] == nil then + table.insert(duplicates[v.lhs], v2) + seen[v.lhs][v2] = true + end end end end + return duplicates end diff --git a/tests/hawtkeys/dupes_spec.lua b/tests/hawtkeys/dupes_spec.lua new file mode 100644 index 0000000..d592bb9 --- /dev/null +++ b/tests/hawtkeys/dupes_spec.lua @@ -0,0 +1,65 @@ +local hawtkeys = require("hawtkeys") +local util = require("hawtkeys.utils") +local ts = require("hawtkeys.ts") +---@diagnostic disable-next-line: undefined-field +local eq = assert.are.same + +describe("HawtkeysDupes", function() + before_each(function() + require("plenary.reload").reload_module("hawtkeys") + ts.reset_scanned_files() + hawtkeys.setup({}) + end) + + it("should detect duplicates", function() + local keymap = ts.find_maps_in_file( + "tests/hawtkeys/example_configs/duplicates.lua" + ) + + eq("n", keymap[1].mode) + eq("t", keymap[1].lhs) + + eq("n", keymap[2].mode) + eq("t", keymap[2].lhs) + + eq("x", keymap[3].mode) + eq("f", keymap[3].lhs) + + eq("x", keymap[4].mode) + eq("f", keymap[4].lhs) + + local dupes = util.find_duplicates(keymap) + + eq("n", dupes["t"][1].mode) + eq("n", dupes["t"][2].mode) + + eq("x", dupes["f"][1].mode) + eq("x", dupes["f"][2].mode) + end) + + it( + "should detect duplicates with partial mode matches for multi-mode maps", + function() + local keymap = ts.find_maps_in_file( + "tests/hawtkeys/example_configs/multi_mode.lua" + ) + eq({ "n", "x" }, keymap[1].mode) + eq("2", keymap[1].lhs) + eq(':echo "hello"', keymap[1].rhs) + + eq({ "x", "v" }, keymap[2].mode) + eq("2", keymap[2].lhs) + eq(':echo "hello2"', keymap[2].rhs) + + eq("v", keymap[3].mode) + eq("2", keymap[3].lhs) + eq(':echo "hello3"', keymap[3].rhs) + + local dupes = util.find_duplicates(keymap) + + eq({ "n", "x" }, dupes["2"][1].mode) + eq({ "x", "v" }, dupes["2"][2].mode) + eq("v", dupes["2"][3].mode) + end + ) +end) diff --git a/tests/hawtkeys/example_configs/duplicates.lua b/tests/hawtkeys/example_configs/duplicates.lua new file mode 100644 index 0000000..7112132 --- /dev/null +++ b/tests/hawtkeys/example_configs/duplicates.lua @@ -0,0 +1,7 @@ +vim.keymap.set("n", "t", ":term", {}) + +vim.api.nvim_set_keymap("n", "t", "ToggleTerm", {}) + +vim.keymap.set("x", "f", ':echo "test"', {}) + +vim.api.nvim_set_keymap("x", "f", 'echo "test"', {}) diff --git a/tests/hawtkeys/example_configs/multi_mode.lua b/tests/hawtkeys/example_configs/multi_mode.lua new file mode 100644 index 0000000..1466702 --- /dev/null +++ b/tests/hawtkeys/example_configs/multi_mode.lua @@ -0,0 +1,5 @@ +vim.keymap.set({ "n", "x" }, "2", ':echo "hello"') + +vim.keymap.set({ "x", "v" }, "2", ':echo "hello2"') + +vim.keymap.set("v", "2", ':echo "hello3"') diff --git a/tests/hawtkeys/ts_spec.lua b/tests/hawtkeys/ts_spec.lua index bfb2044..b033906 100644 --- a/tests/hawtkeys/ts_spec.lua +++ b/tests/hawtkeys/ts_spec.lua @@ -72,6 +72,19 @@ describe("Treesitter can extract keymaps", function() eq(':echo "hello"', keymap[1].rhs) end) + it("extract multi-mode keymaps", function() + local keymap = ts.find_maps_in_file( + "tests/hawtkeys/example_configs/multi_mode.lua" + ) + eq({ "n", "x" }, keymap[1].mode) + eq("2", keymap[1].lhs) + eq(':echo "hello"', keymap[1].rhs) + + eq({ "x", "v" }, keymap[2].mode) + eq("2", keymap[2].lhs) + eq(':echo "hello2"', keymap[2].rhs) + end) + it("Extract short dot index aliasesd keymap", function() hawtkeys.setup({ customMaps = {