diff --git a/.stylua.toml b/.stylua.toml new file mode 100644 index 0000000..0b2e146 --- /dev/null +++ b/.stylua.toml @@ -0,0 +1,5 @@ +column_width = 80 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 4 +quote_style = "AutoPreferDouble" diff --git a/lua/hawtkeys/init.lua b/lua/hawtkeys/init.lua index dbfa457..bd7cad7 100644 --- a/lua/hawtkeys/init.lua +++ b/lua/hawtkeys/init.lua @@ -4,12 +4,16 @@ function M.setup(config) M.homerow = config.homerow or 2 M.powerFingers = config.powerFingers or { 2, 3, 6, 7 } M.keyboardLayout = config.keyboardLayout or "qwerty" - vim.api.nvim_create_user_command("Hawtkeys", - "lua require('hawtkeys.ui').show()", - {}) - vim.api.nvim_create_user_command("HawtkeysAll", + vim.api.nvim_create_user_command( + "Hawtkeys", + "lua require('hawtkeys.ui').show()", + {} + ) + vim.api.nvim_create_user_command( + "HawtkeysAll", "lua require('hawtkeys.ui').showAll()", - {}) + {} + ) end return M diff --git a/lua/hawtkeys/keyboards.lua b/lua/hawtkeys/keyboards.lua index 2e3f6e4..5f868f6 100644 --- a/lua/hawtkeys/keyboards.lua +++ b/lua/hawtkeys/keyboards.lua @@ -1,5 +1,5 @@ M = {} -- TODO: Make this dynamic, loading from the keyboards directory -M.qwerty = require('hawtkeys.keyboards.qwerty').layout +M.qwerty = require("hawtkeys.keyboards.qwerty").layout return M diff --git a/lua/hawtkeys/keyboards/qwerty.lua b/lua/hawtkeys/keyboards/qwerty.lua index 1c92c16..5dfaa61 100644 --- a/lua/hawtkeys/keyboards/qwerty.lua +++ b/lua/hawtkeys/keyboards/qwerty.lua @@ -1,37 +1,37 @@ --TODO: Mapp all other keys, including modifiers and symbols - local layout = { - q = { finger = 1, row = 1, col = 1 }, - w = { finger = 2, row = 1, col = 2 }, - e = { finger = 3, row = 1, col = 3 }, - r = { finger = 4, row = 1, col = 4 }, - t = { finger = 4, row = 1, col = 5 }, - y = { finger = 6, row = 1, col = 6 }, - u = { finger = 6, row = 1, col = 7 }, - i = { finger = 7, row = 1, col = 8 }, - o = { finger = 7, row = 1, col = 9 }, - p = { finger = 8, row = 1, col = 10 }, +local layout = { + q = { finger = 1, row = 1, col = 1 }, + w = { finger = 2, row = 1, col = 2 }, + e = { finger = 3, row = 1, col = 3 }, + r = { finger = 4, row = 1, col = 4 }, + t = { finger = 4, row = 1, col = 5 }, + y = { finger = 6, row = 1, col = 6 }, + u = { finger = 6, row = 1, col = 7 }, + i = { finger = 7, row = 1, col = 8 }, + o = { finger = 7, row = 1, col = 9 }, + p = { finger = 8, row = 1, col = 10 }, - a = { finger = 1, row = 2, col = 1 }, - s = { finger = 2, row = 2, col = 2 }, - d = { finger = 3, row = 2, col = 3 }, - f = { finger = 3, row = 2, col = 4 }, - g = { finger = 3, row = 2, col = 5 }, - h = { finger = 6, row = 2, col = 6 }, - j = { finger = 7, row = 2, col = 7 }, - k = { finger = 7, row = 2, col = 8 }, - l = { finger = 8, row = 2, col = 9 }, + a = { finger = 1, row = 2, col = 1 }, + s = { finger = 2, row = 2, col = 2 }, + d = { finger = 3, row = 2, col = 3 }, + f = { finger = 3, row = 2, col = 4 }, + g = { finger = 3, row = 2, col = 5 }, + h = { finger = 6, row = 2, col = 6 }, + j = { finger = 7, row = 2, col = 7 }, + k = { finger = 7, row = 2, col = 8 }, + l = { finger = 8, row = 2, col = 9 }, - z = { finger = 1, row = 3, col = 1 }, - x = { finger = 3, row = 3, col = 2 }, - c = { finger = 3, row = 3, col = 3 }, - v = { finger = 3, row = 3, col = 4 }, - b = { finger = 6, row = 3, col = 5 }, - n = { finger = 6, row = 3, col = 6 }, - m = { finger = 6, row = 3, col = 7 }, + z = { finger = 1, row = 3, col = 1 }, + x = { finger = 3, row = 3, col = 2 }, + c = { finger = 3, row = 3, col = 3 }, + v = { finger = 3, row = 3, col = 4 }, + b = { finger = 6, row = 3, col = 5 }, + n = { finger = 6, row = 3, col = 6 }, + m = { finger = 6, row = 3, col = 7 }, - [' '] = { finger = 4, row = 4, col = 6 } -- Spacebar - } + [" "] = { finger = 4, row = 4, col = 6 }, -- Spacebar +} - return { - layout = layout - } +return { + layout = layout, +} diff --git a/lua/hawtkeys/score.lua b/lua/hawtkeys/score.lua index d14d2c9..0716b23 100644 --- a/lua/hawtkeys/score.lua +++ b/lua/hawtkeys/score.lua @@ -19,18 +19,40 @@ local function key_score(key1, key2, str, layout) local key1Data = keyboard[key1] local key2Data = keyboard[key2] - -- The higher the score the better local doubleCharBonus = (key1 == key2) and 3 or 0 - local sameFingerPenalty = (key1Data.finger == key2Data.finger) and 1 or 0 - local homerowBonus = (key1Data.row == config.homerow and key2Data.row == 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]) 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]) and - 1 or 0 + local sameFingerPenalty = (key1Data.finger == key2Data.finger) and 1 + or 0 + local homerowBonus = ( + key1Data.row == config.homerow + and key2Data.row == 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] + ) + 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] + ) + and 1 + or 0 local mnemonicBonus = Mnemonic_score(key1, key2, str) - local score = (doubleCharBonus + homerowBonus + powerFinger1Bonus + powerFinger2Bonus + mnemonicBonus) - - sameFingerPenalty + local score = ( + doubleCharBonus + + homerowBonus + + powerFinger1Bonus + + powerFinger2Bonus + + mnemonicBonus + ) - sameFingerPenalty return score else @@ -72,7 +94,7 @@ local function generate_combos(str) local pairs = {} local len = #str for i = 1, len - 1 do - local char1 = str:sub(i, i):lower() -- Convert characters to lowercase + local char1 = str:sub(i, i):lower() -- Convert characters to lowercase for j = i + 1, len do local char2 = str:sub(j, j):lower() -- Convert characters to lowercase table.insert(pairs, char1 .. char2) @@ -100,7 +122,6 @@ local function process_string(str) end table.sort(sortedScores, utils.Score_sort) - local already_used_keys = tsSearch.get_all_keymaps() local find_mapping = function(maps, lhs) @@ -113,13 +134,17 @@ local function process_string(str) end for i = #sortedScores, 1, -1 do - if find_mapping(already_used_keys, '' .. sortedScores[i].combo) then - local mapping = find_mapping(already_used_keys, '' .. sortedScores[i].combo) + if + find_mapping(already_used_keys, "" .. sortedScores[i].combo) + then + local mapping = find_mapping( + already_used_keys, + "" .. sortedScores[i].combo + ) sortedScores[i].already_mapped = mapping end end - return sortedScores end @@ -132,10 +157,14 @@ local function highlight_desc(str, combo) local marked = {} -- Keep track of characters already marked for i = 1, #combo do local char = combo:sub(i, i) - local pos = marked[char] or 1 -- Start searching from the last marked position or from the beginning + local pos = marked[char] or 1 -- Start searching from the last marked position or from the beginning pos = newStr:find(char, pos, true) or 0 if pos then - newStr = newStr:sub(1, pos - 1) .. "[" .. char .. "]" .. newStr:sub(pos + 1) + newStr = newStr:sub(1, pos - 1) + .. "[" + .. char + .. "]" + .. newStr:sub(pos + 1) marked[char] = pos + 2 -- Mark this character's position end end @@ -148,20 +177,29 @@ local function scoreTable(str) -- local results = utils.top5(process_string(str)) local results = process_string(str) local resultTable = {} - for _, data in ipairs(results) - do - table.insert(resultTable, - "Key: " .. - highlight_desc(str, data.combo) .. - "" .. - data.combo .. - " - Hawt Score: " .. - data.score) - if data.already_mapped ~= nil and data.already_mapped.rhs ~= nil and data.already_mapped.from_file ~= nil then - table.insert(resultTable, - "Already mapped: " .. tostring(data.already_mapped.rhs)) - table.insert(resultTable, - "In File" .. (data.already_mapped.from_file)) + for _, data in ipairs(results) do + table.insert( + resultTable, + "Key: " + .. highlight_desc(str, data.combo) + .. "" + .. data.combo + .. " - Hawt Score: " + .. data.score + ) + if + data.already_mapped ~= nil + and data.already_mapped.rhs ~= nil + and data.already_mapped.from_file ~= nil + then + table.insert( + resultTable, + "Already mapped: " .. tostring(data.already_mapped.rhs) + ) + table.insert( + resultTable, + "In File" .. data.already_mapped.from_file + ) end end return resultTable diff --git a/lua/hawtkeys/show_all.lua b/lua/hawtkeys/show_all.lua index fa0c02b..788e65d 100644 --- a/lua/hawtkeys/show_all.lua +++ b/lua/hawtkeys/show_all.lua @@ -5,9 +5,17 @@ local tsSearch = require("hawtkeys.ts") function M.show_all() local allKeys = tsSearch.get_all_keymaps() local resultTable = {} - for _, data in ipairs(allKeys) - do - table.insert(resultTable, tostring(data.lhs) .. " " .. tostring(data.rhs) .. " (" .. tostring(data.mode) .. ") - " .. tostring(data.from_file)) + for _, data in ipairs(allKeys) do + table.insert( + resultTable, + tostring(data.lhs) + .. " " + .. tostring(data.rhs) + .. " (" + .. tostring(data.mode) + .. ") - " + .. tostring(data.from_file) + ) end return resultTable end diff --git a/lua/hawtkeys/ts.lua b/lua/hawtkeys/ts.lua index f62757e..6703811 100644 --- a/lua/hawtkeys/ts.lua +++ b/lua/hawtkeys/ts.lua @@ -1,8 +1,8 @@ local M = {} -local Path = require('plenary.path') -local scan = require('plenary.scandir') -local utils = require('hawtkeys.utils') -local config = require('hawtkeys') +local Path = require("plenary.path") +local scan = require("plenary.scandir") +local utils = require("hawtkeys.utils") +local config = require("hawtkeys") local ts = require("nvim-treesitter.compat") local ts_query = require("nvim-treesitter.query") @@ -10,7 +10,7 @@ local return_keymaps = {} ---@param dir string ---@return table local function find_files(dir) - print('Scanning dir' .. dir) + print("Scanning dir" .. dir) local dirScan = dir or vim.fn.stdpath("config") local files = scan.scan_dir(dirScan, { hidden = true }) return files @@ -25,27 +25,38 @@ local function find_maps_in_file(file_path) return {} end local file_content = Path:new(file_path):read() - local parser = vim.treesitter.get_string_parser(file_content, 'lua', {}) -- Get the Lua parser - local tree = parser:parse()[1]:root() - local ts_keymaps = {} + local parser = vim.treesitter.get_string_parser(file_content, "lua", {}) -- Get the Lua parser + local tree = parser:parse()[1]:root() + local ts_keymaps = {} -- TODO: This currently doesnt always work, as the options for helper functions are different, -- need to use TS to resolve it back to a native keymap function - local query = ts.parse_query("lua", [[ - (function_call - name: (dot_index_expression) @exp (#any-of? @exp "vim.api.nvim_set_keymap" "vim.keymap.set") - (arguments) @args - ) -]]) - for match in ts_query.iter_prepared_matches(query, tree, file_content, 0, -1) do + local query = ts.parse_query( + "lua", + [[ + (function_call + name: (dot_index_expression) @exp (#any-of? @exp "vim.api.nvim_set_keymap" "vim.keymap.set") + (arguments) @args + ) + ]] + ) + for match in + ts_query.iter_prepared_matches(query, tree, file_content, 0, -1) + do for type, node in pairs(match) do if type == "args" then table.insert(ts_keymaps, { - mode = vim.treesitter.get_node_text(node.node:child(1), file_content):gsub("^%s*(['\"])(.*)%1%s*$", - "%2"):gsub("[\n\r]", ""), - lhs = vim.treesitter.get_node_text(node.node:child(3), file_content):gsub("^%s*(['\"])(.*)%1%s*$", - "%2"):gsub("[\n\r]", ""), - rhs = vim.treesitter.get_node_text(node.node:child(5), file_content):gsub("^%s*(['\"])(.*)%1%s*$", - "%2"):gsub("[\n\r]", ""), + mode = vim.treesitter + .get_node_text(node.node:child(1), file_content) + :gsub("^%s*(['\"])(.*)%1%s*$", "%2") + :gsub("[\n\r]", ""), + lhs = vim.treesitter + .get_node_text(node.node:child(3), file_content) + :gsub("^%s*(['\"])(.*)%1%s*$", "%2") + :gsub("[\n\r]", ""), + rhs = vim.treesitter + .get_node_text(node.node:child(5), file_content) + :gsub("^%s*(['\"])(.*)%1%s*$", "%2") + :gsub("[\n\r]", ""), from_file = file_path, }) end @@ -59,7 +70,7 @@ end local function get_keymaps_from_vim() local vim_keymaps = {} - local vim_keymaps_raw = vim.api.nvim_get_keymap('n') + local vim_keymaps_raw = vim.api.nvim_get_keymap("n") print("Collecting vim keymaps") for _, vim_keymap in ipairs(vim_keymaps_raw) do table.insert(vim_keymaps, { @@ -67,7 +78,7 @@ local function get_keymaps_from_vim() -- TODO: leader subsitiution as vim keymaps contain raw leader lhs = vim_keymap.lhs:gsub(config.leader, ""), rhs = vim_keymap.rhs, - from_file = 'Vim Defaults' + from_file = "Vim Defaults", }) end return vim_keymaps diff --git a/lua/hawtkeys/ui.lua b/lua/hawtkeys/ui.lua index 62b50eb..e292c72 100644 --- a/lua/hawtkeys/ui.lua +++ b/lua/hawtkeys/ui.lua @@ -1,94 +1,123 @@ M = {} -Hawtkeys = require('hawtkeys.score') -ShowAll = require('hawtkeys.show_all') +Hawtkeys = require("hawtkeys.score") +ShowAll = require("hawtkeys.show_all") M.search = function(text) - local returnText = Hawtkeys.ScoreTable(text) - vim.api.nvim_buf_set_lines(ResultBuf, 0, -1, false, returnText) + local returnText = Hawtkeys.ScoreTable(text) + vim.api.nvim_buf_set_lines(ResultBuf, 0, -1, false, returnText) - --loop lines and hilight if already mapped: - for i, line in ipairs(returnText) do - if string.match(line, "^Already mapped:.*") then - vim.api.nvim_buf_add_highlight(ResultBuf, -1, "ErrorMsg", i - 1, 0, -1) - vim.api.nvim_buf_add_highlight(ResultBuf, -1, "ErrorMsg", i, 0, -1) - vim.api.nvim_buf_add_highlight(ResultBuf, -1, "ErrorMsg", i - 2, 0, -1) + --loop lines and hilight if already mapped: + for i, line in ipairs(returnText) do + if string.match(line, "^Already mapped:.*") then + vim.api.nvim_buf_add_highlight( + ResultBuf, + -1, + "ErrorMsg", + i - 1, + 0, + -1 + ) + vim.api.nvim_buf_add_highlight(ResultBuf, -1, "ErrorMsg", i, 0, -1) + vim.api.nvim_buf_add_highlight( + ResultBuf, + -1, + "ErrorMsg", + i - 2, + 0, + -1 + ) + end end - end end M.show = function() - ResultBuf = vim.api.nvim_create_buf(false, true) - local ui = vim.api.nvim_list_uis()[1] - local width = 100 - local height = 30 - ResultWin = vim.api.nvim_open_win(ResultBuf, true, { - relative = "editor", - width = width, - height = height, - col = (ui.width / 2) - (width / 2), - row = (ui.height / 2) - (height / 2), - anchor = "NW", - footer = "Suggested Keybindings", - footer_pos = "center", - border = "single", - noautocmd = true, - }) - local searchBuf = vim.api.nvim_create_buf(false, true) - SearchWin = vim.api.nvim_open_win(searchBuf, true, { - relative = "editor", - width = width, - height = 1, - col = (ui.width / 2) - (width / 2), - row = (ui.height / 2) - (height / 2) - 2, - anchor = "NW", - border = "single", - style = "minimal", - title = "Enter Command Description", - title_pos = "center", - noautocmd = true, - - }) - vim.api.nvim_buf_set_keymap(searchBuf, "i", "", "lua require('hawtkeys.ui').hide()", - { noremap = true, silent = true }) - vim.api.nvim_buf_set_keymap(searchBuf, "n", "", "lua require('hawtkeys.ui').hide()", - { noremap = true, silent = true }) - --disallow new lines in searchBuf - vim.api.nvim_buf_set_keymap(searchBuf, "i", "", "", { noremap = true, silent = true }) - -- subscribe to changed text in searchBuf - vim.api.nvim_buf_attach(searchBuf, false, { - on_lines = function() - vim.schedule(function() - M.search(vim.api.nvim_buf_get_lines(searchBuf, 0, 1, false)[1]) - end) - end - }) - -- - vim.api.nvim_set_current_buf(searchBuf) - vim.api.nvim_command("startinsert") + ResultBuf = vim.api.nvim_create_buf(false, true) + local ui = vim.api.nvim_list_uis()[1] + local width = 100 + local height = 30 + ResultWin = vim.api.nvim_open_win(ResultBuf, true, { + relative = "editor", + width = width, + height = height, + col = (ui.width / 2) - (width / 2), + row = (ui.height / 2) - (height / 2), + anchor = "NW", + footer = "Suggested Keybindings", + footer_pos = "center", + border = "single", + noautocmd = true, + }) + local searchBuf = vim.api.nvim_create_buf(false, true) + SearchWin = vim.api.nvim_open_win(searchBuf, true, { + relative = "editor", + width = width, + height = 1, + col = (ui.width / 2) - (width / 2), + row = (ui.height / 2) - (height / 2) - 2, + anchor = "NW", + border = "single", + style = "minimal", + title = "Enter Command Description", + title_pos = "center", + noautocmd = true, + }) + vim.api.nvim_buf_set_keymap( + searchBuf, + "i", + "", + "lua require('hawtkeys.ui').hide()", + { noremap = true, silent = true } + ) + vim.api.nvim_buf_set_keymap( + searchBuf, + "n", + "", + "lua require('hawtkeys.ui').hide()", + { noremap = true, silent = true } + ) + --disallow new lines in searchBuf + vim.api.nvim_buf_set_keymap( + searchBuf, + "i", + "", + "", + { noremap = true, silent = true } + ) + -- subscribe to changed text in searchBuf + vim.api.nvim_buf_attach(searchBuf, false, { + on_lines = function() + vim.schedule(function() + M.search(vim.api.nvim_buf_get_lines(searchBuf, 0, 1, false)[1]) + end) + end, + }) + -- + vim.api.nvim_set_current_buf(searchBuf) + vim.api.nvim_command("startinsert") end M.showAll = function() - local ui = vim.api.nvim_list_uis()[1] - local width = 100 - local height = 30 - ResultBuf = vim.api.nvim_create_buf(false, true) - ResultWin = vim.api.nvim_open_win(ResultBuf, true, { - relative = "editor", - width = width, - height = height, - col = (ui.width / 2) - (width / 2), - row = (ui.height / 2) - (height / 2), - anchor = "NW", - footer = "Current Keybindings", - footer_pos = "center", - border = "single", - noautocmd = true, - }) - vim.api.nvim_buf_set_lines(ResultBuf, 0, -1, false, ShowAll.show_all()) + local ui = vim.api.nvim_list_uis()[1] + local width = 100 + local height = 30 + ResultBuf = vim.api.nvim_create_buf(false, true) + ResultWin = vim.api.nvim_open_win(ResultBuf, true, { + relative = "editor", + width = width, + height = height, + col = (ui.width / 2) - (width / 2), + row = (ui.height / 2) - (height / 2), + anchor = "NW", + footer = "Current Keybindings", + footer_pos = "center", + border = "single", + noautocmd = true, + }) + vim.api.nvim_buf_set_lines(ResultBuf, 0, -1, false, ShowAll.show_all()) end M.hide = function() - vim.api.nvim_win_close(ResultWin, true) - vim.api.nvim_win_close(SearchWin, true) - vim.api.nvim_command("stopinsert") + vim.api.nvim_win_close(ResultWin, true) + vim.api.nvim_win_close(SearchWin, true) + vim.api.nvim_command("stopinsert") end return M