From 344596919f8c9e564d9345ad9cace1273c13ee26 Mon Sep 17 00:00:00 2001 From: tris203 Date: Thu, 21 Dec 2023 16:53:06 +0000 Subject: [PATCH 1/3] refactor(init): move config into object and add tests --- lua/hawtkeys/init.lua | 72 +++++++++++++++----------------- lua/hawtkeys/score.lua | 26 ++++++------ lua/hawtkeys/ts.lua | 18 ++++---- tests/hawtkeys/init_spec.lua | 79 ++++++++++++++++++++++++++++++++++++ 4 files changed, 135 insertions(+), 60 deletions(-) create mode 100644 tests/hawtkeys/init_spec.lua diff --git a/lua/hawtkeys/init.lua b/lua/hawtkeys/init.lua index 47ea44b..57e6e01 100644 --- a/lua/hawtkeys/init.lua +++ b/lua/hawtkeys/init.lua @@ -46,55 +46,49 @@ local _defaultSet = { }, -- method 6 } +M.defaultConfig = { + leader = " ", + homerow = 2, + powerFingers = { 2, 3, 6, 7 }, + keyboardLayout = "qwerty", + keyMapSet = _defaultSet, + highlights = { + HawtkeysMatchGreat = { link = "DiagnosticOk", underline = true }, + HawtkeysMatchGood = { link = "DiagnosticOk" }, + HawtkeysMatchOk = { link = "DiagnosticWarn" }, + HawtkeysMatchBad = { link = "DiagnosticError" }, + }, +} + ---@param config HawtKeyPartialConfig function M.setup(config) - config = config or {} - M.leader = config.leader or " " - M.homerow = config.homerow or 2 - M.powerFingers = config.powerFingers or { 2, 3, 6, 7 } - M.keyboardLayout = config.keyboardLayout or "qwerty" - M.keyMapSet = vim.tbl_extend("force", _defaultSet, config.customMaps or {}) + config = vim.tbl_deep_extend("force", M.defaultConfig, config or {}) - local default_match_great - if not config.highlights or not config.highlights.HawtkeysMatchGreat then - default_match_great = - vim.api.nvim_get_hl(0, { name = "DiagnosticOk", link = false }) - default_match_great.underline = true + config.keyMapSet = + vim.tbl_deep_extend("force", _defaultSet, config.customMaps or {}) + config.customMaps = nil + local appliedHighlights = {} + for name, props in pairs(config.highlights) do + local styleConfig = + vim.api.nvim_get_hl(0, { name = props.link, link = false }) + for k, v in pairs(props) do + if k == "link" then + break + end + styleConfig[k] = v + end + appliedHighlights[name] = styleConfig end - M.highlights = vim.tbl_extend("keep", config.highlights or {}, { - HawtkeysMatchGreat = default_match_great, - HawtkeysMatchGood = { - link = "DiagnosticOk", - }, - HawtkeysMatchOk = { - link = "DiagnosticWarn", - }, - HawtkeysMatchBad = { - link = "DiagnosticError", - }, - }) - for name, hl in pairs(M.highlights) do + M.config = config + + for name, hl in pairs(appliedHighlights) do vim.api.nvim_set_hl(0, name, hl) end vim.api.nvim_create_autocmd("ColorScheme", { callback = function() - if default_match_great then - for k, v in - pairs( - vim.api.nvim_get_hl( - 0, - { name = "DiagnosticOk", link = false } - ) - ) - do - default_match_great[k] = v - end - default_match_great.underline = true - end - - for name, hl in pairs(M.highlights) do + for name, hl in pairs(appliedHighlights) do vim.api.nvim_set_hl(0, name, hl) end end, diff --git a/lua/hawtkeys/score.lua b/lua/hawtkeys/score.lua index 838904e..87ee13e 100644 --- a/lua/hawtkeys/score.lua +++ b/lua/hawtkeys/score.lua @@ -1,4 +1,4 @@ -local config = require("hawtkeys") +local hawtkeys = require("hawtkeys") local keyboardLayouts = require("hawtkeys.keyboards") local tsSearch = require("hawtkeys.ts") local utils = require("hawtkeys.utils") @@ -51,24 +51,24 @@ local function key_score(key1, key2, str, layout) local sameFingerPenalty = (key1Data.finger == key2Data.finger) and 1 or 0 local homerowBonus = ( - key1Data.row == config.homerow - and key2Data.row == config.homerow + key1Data.row == hawtkeys.config.homerow + and key2Data.row == hawtkeys.config.homerow ) and 1 or 0 local powerFinger1Bonus = ( - key1Data.finger == config.powerFingers[1] - or key1Data.finger == config.powerFingers[2] - or key1Data.finger == config.powerFingers[3] - or key1Data.finger == config.powerFingers[4] + key1Data.finger == hawtkeys.config.powerFingers[1] + or key1Data.finger == hawtkeys.config.powerFingers[2] + or key1Data.finger == hawtkeys.config.powerFingers[3] + or key1Data.finger == hawtkeys.config.powerFingers[4] ) and 1 or 0 local powerFinger2Bonus = ( - key2Data.finger == config.powerFingers[1] - or key2Data.finger == config.powerFingers[2] - or key2Data.finger == config.powerFingers[3] - or key2Data.finger == config.powerFingers[4] + key2Data.finger == hawtkeys.config.powerFingers[1] + or key2Data.finger == hawtkeys.config.powerFingers[2] + or key2Data.finger == hawtkeys.config.powerFingers[3] + or key2Data.finger == hawtkeys.config.powerFingers[4] ) and 1 or 0 @@ -91,7 +91,7 @@ end ---@param str string ---@return table local function generate_combos(str) - str = str:gsub(config.leader, "") + str = str:gsub(hawtkeys.config.leader, "") local pairs = {} local len = #str for i = 1, len - 1 do @@ -112,7 +112,7 @@ local function find_matches(str) for _, combo in ipairs(combinations) do local a, b = combo:sub(1, 1), combo:sub(2, 2) - local score = key_score(a, b, str, config.keyboardLayout) + local score = key_score(a, b, str, hawtkeys.config.keyboardLayout) scores[combo] = score end diff --git a/lua/hawtkeys/ts.lua b/lua/hawtkeys/ts.lua index b11b35e..1d8d0ae 100644 --- a/lua/hawtkeys/ts.lua +++ b/lua/hawtkeys/ts.lua @@ -2,7 +2,7 @@ local M = {} local Path = require("plenary.path") local scan = require("plenary.scandir") local utils = require("hawtkeys.utils") -local config = require("hawtkeys") +local hawtkeys = require("hawtkeys") local ts = require("nvim-treesitter.compat") local tsQuery = require("nvim-treesitter.query") @@ -135,7 +135,7 @@ local function find_maps_in_file(filePath) -- need to use TS to resolve it back to a native keymap local dotIndexExpressionQuery = ts.parse_query( "lua", - build_dot_index_expression_query(config.keyMapSet) + build_dot_index_expression_query(hawtkeys.config.keyMapSet) ) for match in tsQuery.iter_prepared_matches( @@ -152,7 +152,7 @@ local function find_maps_in_file(filePath) node.node:parent():child(0), fileContent ) - local mapDef = config.keyMapSet[parent] + local mapDef = hawtkeys.config.keyMapSet[parent] ---@type string local mode = return_field_data( node.node, @@ -219,8 +219,10 @@ local function find_maps_in_file(filePath) end end - local functionCallQuery = - ts.parse_query("lua", build_function_call_query(config.keyMapSet)) + local functionCallQuery = ts.parse_query( + "lua", + build_function_call_query(hawtkeys.config.keyMapSet) + ) for match in tsQuery.iter_prepared_matches( @@ -237,7 +239,7 @@ local function find_maps_in_file(filePath) node.node:parent():child(0), fileContent ) - local mapDef = config.keyMapSet[parent] + local mapDef = hawtkeys.config.keyMapSet[parent] ---@type string local mode = return_field_data( node.node, @@ -306,7 +308,7 @@ local function find_maps_in_file(filePath) end local whichKeyQuery = - ts.parse_query("lua", build_which_key_query(config.keyMapSet)) + ts.parse_query("lua", build_which_key_query(hawtkeys.config.keyMapSet)) for match in tsQuery.iter_prepared_matches(whichKeyQuery, tree, fileContent, 0, -1) @@ -359,7 +361,7 @@ local function get_keymaps_from_vim() table.insert(vimKeymaps, { mode = vimKeymap.mode, -- TODO: leader subsitiution as vim keymaps contain raw leader - lhs = vimKeymap.lhs:gsub(config.leader, ""), + lhs = vimKeymap.lhs:gsub(hawtkeys.config.leader, ""), rhs = vimKeymap.rhs, from_file = "Vim Defaults", }) diff --git a/tests/hawtkeys/init_spec.lua b/tests/hawtkeys/init_spec.lua new file mode 100644 index 0000000..6856aee --- /dev/null +++ b/tests/hawtkeys/init_spec.lua @@ -0,0 +1,79 @@ +local hawtkeys = require("hawtkeys") +---@diagnostic disable-next-line: undefined-field +local eq = assert.are.same +---@diagnostic disable-next-line: undefined-field +local falsy = assert.falsy +local userCommands = { + ["Hawtkeys"] = "lua require('hawtkeys.ui').show()", + ["HawtkeysAll"] = "lua require('hawtkeys.ui').show_all()", + ["HawtkeysDupes"] = "lua require('hawtkeys.ui').show_dupes()", +} + +describe("set up function", function() + before_each(function() + require("plenary.reload").reload_module("hawtkeys") + for _, command in ipairs(userCommands) do + vim.api.nvim_command("silent! delcommand " .. command) + end + end) + it("should set up the default config", function() + hawtkeys.setup({}) + eq(hawtkeys.defaultConfig, hawtkeys.config) + end) + + it("should be able to override the default config", function() + hawtkeys.setup({ leader = "," }) + eq(",", hawtkeys.config.leader) + end) + + it("can set custom highlights", function() + hawtkeys.setup({ + highlights = { + HawtkeysMatchGreat = { + link = "DiagnosticSomethingElse", + }, + }, + }) + eq( + "DiagnosticSomethingElse", + hawtkeys.config.highlights.HawtkeysMatchGreat.link + ) + eq( + hawtkeys.defaultConfig.highlights.HawtkeysMatchGood, + hawtkeys.config.highlights.HawtkeysMatchGood + ) + end) + + it("can pass in custom mapping definitions", function() + hawtkeys.setup({ + customMaps = { + ["custom.map"] = { + method = "dot_index_expression", + lhsIndex = 1, + rhsIndex = 2, + modeIndex = "n", + optsIndex = 3, + }, + }, + }) + eq("dot_index_expression", hawtkeys.config.keyMapSet["custom.map"].method) + eq(1, hawtkeys.config.keyMapSet["custom.map"].lhsIndex) + eq(2, hawtkeys.config.keyMapSet["custom.map"].rhsIndex) + eq("n", hawtkeys.config.keyMapSet["custom.map"].modeIndex) + eq(3, hawtkeys.config.keyMapSet["custom.map"].optsIndex) + end) + + it("User commands should be available after setup", function() + local commandspresetup = vim.api.nvim_get_commands({}) + hawtkeys.setup({}) + local commandsPostSetup = vim.api.nvim_get_commands({}) + -- Check that the commands are not present before setup + for command, _ in ipairs(userCommands) do + falsy(commandspresetup[command]) + end + -- Check that the commands are present after setup + for command, action in ipairs(userCommands) do + eq(action, commandsPostSetup[command].definition) + end + end) +end) From bd924efde62ce6da096ecc9a18ef16a69f259e81 Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Thu, 21 Dec 2023 12:42:49 -0800 Subject: [PATCH 2/3] refactor: make default config local fix: recompute theme on Colorscheme event refactor: make default config read-only, merge configs into new table refactor: add type for the Hawtkeys root module refactor: rename SupportedKeyboardLayouts to HawtKeySupportedKeyboardLayouts --- lua/hawtkeys/init.lua | 75 ++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 29 deletions(-) diff --git a/lua/hawtkeys/init.lua b/lua/hawtkeys/init.lua index 57e6e01..fdfa2f9 100644 --- a/lua/hawtkeys/init.lua +++ b/lua/hawtkeys/init.lua @@ -1,13 +1,17 @@ +---@class Hawtkeys +---@field config HawtKeyConfig local M = {} ----@alias SupportedKeyboardLayouts "qwerty" | "dvorak" +---@alias HawtKeySupportedKeyboardLayouts "qwerty" | "dvorak" ---@class HawtKeyConfig ---@field leader string ---@field homerow number ---@field powerFingers number[] ----@field keyboardLayout SupportedKeyboardLayouts +---@field keyboardLayout HawtKeySupportedKeyboardLayouts +---@field keyMapSet { [string] : TSKeyMapArgs | WhichKeyMapargs } | nil ---@field customMaps { [string] : TSKeyMapArgs | WhichKeyMapargs } | nil +---@field highlights HawtKeyHighlights | nil ---@class HawtKeyHighlights ---@field HawtkeysMatchGreat vim.api.keyset.highlight | nil @@ -19,13 +23,11 @@ local M = {} ---@field leader string | nil ---@field homerow number | nil ---@field powerFingers number[] | nil ----@field keyboardLayout SupportedKeyboardLayouts | nil +---@field keyboardLayout HawtKeySupportedKeyboardLayouts | nil ---@field customMaps { [string] : TSKeyMapArgs | WhichKeyMapargs } | nil ---@field highlights HawtKeyHighlights | nil ---- ---@type { [string] : TSKeyMapArgs | WhichKeyMapargs }--- - local _defaultSet = { ["vim.keymap.set"] = { modeIndex = 1, @@ -46,7 +48,7 @@ local _defaultSet = { }, -- method 6 } -M.defaultConfig = { +local defaultConfig = { leader = " ", homerow = 2, powerFingers = { 2, 3, 6, 7 }, @@ -60,38 +62,53 @@ M.defaultConfig = { }, } ----@param config HawtKeyPartialConfig -function M.setup(config) - config = vim.tbl_deep_extend("force", M.defaultConfig, config or {}) +local function apply_highlights() + for name, props in pairs(M.config.highlights) do + local styleConfig + if props.link then + styleConfig = vim.api.nvim_get_hl(0, { + name = props.link, + link = false, + }) + else + styleConfig = {} + end - config.keyMapSet = - vim.tbl_deep_extend("force", _defaultSet, config.customMaps or {}) - config.customMaps = nil - local appliedHighlights = {} - for name, props in pairs(config.highlights) do - local styleConfig = - vim.api.nvim_get_hl(0, { name = props.link, link = false }) for k, v in pairs(props) do - if k == "link" then - break + if k ~= "link" then + styleConfig[k] = v end - styleConfig[k] = v end - appliedHighlights[name] = styleConfig + vim.api.nvim_set_hl(0, name, styleConfig) end +end - M.config = config - - for name, hl in pairs(appliedHighlights) do - vim.api.nvim_set_hl(0, name, hl) +---@param config HawtKeyPartialConfig +function M.setup(config) + M.config = M.config or {} + for k, default in pairs(defaultConfig) do + local v = config[k] + if k == "highlights" then + -- shallow merge to preserve highlight values + M.config[k] = + vim.tbl_extend("force", defaultConfig.highlights, v or {}) + elseif k == "keyMapSet" then + M.config[k] = vim.tbl_deep_extend( + "force", + defaultConfig.keyMapSet, + config.customMaps or {} + ) + elseif type(default) == "table" then + M.config[k] = vim.tbl_deep_extend("force", default, v or {}) + else + M.config[k] = v or default + end end + apply_highlights() + vim.api.nvim_create_autocmd("ColorScheme", { - callback = function() - for name, hl in pairs(appliedHighlights) do - vim.api.nvim_set_hl(0, name, hl) - end - end, + callback = apply_highlights, }) vim.api.nvim_create_user_command( From b0ce639a2eb1bc4ce827abee2df6a59a1a7bb87c Mon Sep 17 00:00:00 2001 From: Will Hopkins Date: Thu, 21 Dec 2023 12:59:10 -0800 Subject: [PATCH 3/3] fix(tests): export default config via metatable --- lua/hawtkeys/init.lua | 9 +++++++++ tests/hawtkeys/init_spec.lua | 7 ++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lua/hawtkeys/init.lua b/lua/hawtkeys/init.lua index fdfa2f9..8e48fad 100644 --- a/lua/hawtkeys/init.lua +++ b/lua/hawtkeys/init.lua @@ -1,5 +1,6 @@ ---@class Hawtkeys ---@field config HawtKeyConfig +---@field package defaultConfig HawtKeyConfig local M = {} ---@alias HawtKeySupportedKeyboardLayouts "qwerty" | "dvorak" @@ -128,4 +129,12 @@ function M.setup(config) ) end +setmetatable(M, { + __index = function(_, k) + if k == "defaultConfig" then + return vim.deepcopy(defaultConfig) + end + end, +}) + return M diff --git a/tests/hawtkeys/init_spec.lua b/tests/hawtkeys/init_spec.lua index 6856aee..c6cd05b 100644 --- a/tests/hawtkeys/init_spec.lua +++ b/tests/hawtkeys/init_spec.lua @@ -18,6 +18,7 @@ describe("set up function", function() end) it("should set up the default config", function() hawtkeys.setup({}) + ---@diagnostic disable-next-line: invisible eq(hawtkeys.defaultConfig, hawtkeys.config) end) @@ -39,6 +40,7 @@ describe("set up function", function() hawtkeys.config.highlights.HawtkeysMatchGreat.link ) eq( + ---@diagnostic disable-next-line: invisible hawtkeys.defaultConfig.highlights.HawtkeysMatchGood, hawtkeys.config.highlights.HawtkeysMatchGood ) @@ -56,7 +58,10 @@ describe("set up function", function() }, }, }) - eq("dot_index_expression", hawtkeys.config.keyMapSet["custom.map"].method) + eq( + "dot_index_expression", + hawtkeys.config.keyMapSet["custom.map"].method + ) eq(1, hawtkeys.config.keyMapSet["custom.map"].lhsIndex) eq(2, hawtkeys.config.keyMapSet["custom.map"].rhsIndex) eq("n", hawtkeys.config.keyMapSet["custom.map"].modeIndex)