Skip to content

Commit

Permalink
feat: experimental mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
tris203 committed Dec 19, 2023
1 parent 9a68b37 commit 449c1e8
Show file tree
Hide file tree
Showing 3 changed files with 185 additions and 24 deletions.
30 changes: 30 additions & 0 deletions lua/hawtkeys/tests/set_maps.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
vim.api.nvim_set_keymap('n', '<leader>1', ':lua print("1 pressed")<CR>', { noremap = true, silent = true })
vim.keymap.set('n', '<leader>2', ':lua print("2 pressed")<CR>', { noremap = true, silent = true })

local normalMap = function(lhs, rhs)
vim.api.nvim_set_keymap('n', lhs, rhs, { noremap = true, silent = true })
end
normalMap('<leader>3', ':lua print("3 pressed")<CR>')

local kmap = vim.api

kmap.nvim_set_keymap('n', '<leader>4', ':lua print("4 pressed")<CR>', { noremap = true, silent = true })

local nmap = vim.api.nvim_set_keymap

nmap('n', '<leader>5', ':lua print("5 pressed")<CR>', { noremap = true, silent = true })

local whichkey = require('which-key')

whichkey.register({
['<leader>'] = {
['6'] = { ':lua print("6 pressed")<CR>', 'Print 6' },
},
})

{ "plugin/example",
keys = {
{ "<leader>7", ":lua print('7 pressed" },
}
}

4 changes: 4 additions & 0 deletions lua/hawtkeys/tests/test_ts.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
local ts = require("hawtkeys.ts")

local maps = ts.find_maps_in_file("lua/hawtkeys/tests/set_maps.lua")
print(vim.inspect(maps))
175 changes: 151 additions & 24 deletions lua/hawtkeys/ts.lua
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,93 @@ local config = require("hawtkeys")
local ts = require("nvim-treesitter.compat")
local tsQuery = require("nvim-treesitter.query")


---@alias vimModes 'n' | 'x' | 'v' | 'i'

---@alias setMethods 'dot_index_expression' | 'index_expression' | 'function_call'

---@class keyMapArgs
---@field modeIndex number | vimModes
---@field lhsIndex number
---@field rhsIndex number
---@field optsIndex number|nil
---@field method setMethods


---@type { [string] : keyMapArgs }
local keyMapSet = {
["vim.keymap.set"] = { modeIndex = 1, lhsIndex = 2, rhsIndex = 3, optsIndex = 4, method = "dot_index_expression" }, --method 1
["vim.api.nvim_set_keymap"] = { modeIndex = 1, lhsIndex = 2, rhsIndex = 3, optsIndex = 4, method = 'dot_index_expression' }, --method 2
["normalMap"] = { modeIndex = 'n', lhsIndex = 1, rhsIndex = 2, method = 'function_call' }, --method 3
["kmap.nvim_set_keymap"] = { modeIndex = 1, lhsIndex = 2, rhsIndex = 3, method = 'dot_index_expression' }, --method 4
["nmap"] = { modeIndex = 'n', lhsIndex = 1, rhsIndex = 2, method = 'function_call' } -- method 5
}

---@type table<string, boolean>
local scannedFiles = {}

---@param mapDefs keyMapArgs[]
---@return string
local function build_dot_index_expression_query(mapDefs)
local query = "(function_call"
query = query .. "\nname: (dot_index_expression) @exp (#any-of? @exp "
for name, opts in pairs(mapDefs) do
if opts.method == "dot_index_expression" then
query = query .. ' "' .. name .. '"'
end
end
query = query .. ")"
query = query .. "\n(arguments) @args)"
return query
end

---@param mapDefs keyMapArgs[]
---@return string
local function build_function_call_query(mapDefs)
local query = "(function_call"
query = query .. "\nname: (identifier) @exp (#any-of? @exp "
for name, opts in pairs(mapDefs) do
if opts.method == "function_call" then
query = query .. ' "' .. name .. '"'
end
end
query = query .. ")"
query = query .. "\n(arguments) @args)"
return query
end

---@param node TSNode
---@param indexData keyMapArgs
---@param targetData string
---@param file_content string
---@return string
local function return_field_data(node, indexData, targetData, file_content)
---@param i number
---@return number
local function index_offset(i)
return (2 * i) - 1
end
local success, result = pcall(function()
if type(indexData[targetData]) == "number" then
local index = index_offset(indexData[targetData])
---@diagnostic disable-next-line: param-type-mismatch
return vim.treesitter.get_node_text(node:child(index), file_content)
else
return tostring(indexData[targetData])
end
end)
if success then
result = result:gsub("[\n\r]", "")
--remove surrounding quotes
result = result:gsub("^\"(.*)\"$", "%1")
--remove single quotes
result = result:gsub("^'(.*)'$", "%1")
return result
else
return "error"
end
end

---@param dir string
---@return table
local function find_files(dir)
Expand All @@ -16,10 +102,10 @@ local function find_files(dir)
return files
end


---@param file_path string
---@return table
local function find_maps_in_file(file_path)
print("Scanning files " .. file_path)
if scannedFiles[file_path] then
print("Already scanned")
return {}
Expand All @@ -34,21 +120,69 @@ local function find_maps_in_file(file_path)
local tree = parser:parse()[1]:root()
local tsKemaps = {}
-- 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(
-- need to use TS to resolve it back to a native keymap
local dot_index_expression_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
)
]]
build_dot_index_expression_query(keyMapSet)
)
for match in tsQuery.iter_prepared_matches(query, tree, file_content, 0, -1) do
for match in tsQuery.iter_prepared_matches(dot_index_expression_query, tree, file_content, 0, -1) do
for type, node in pairs(match) do
if type == "args" then
local parent = vim.treesitter.get_node_text(node.node:parent():child(0), file_content)
local mapDef = keyMapSet[parent]
---@type string
local mode = return_field_data(node.node, mapDef, "modeIndex", file_content)

---@type string
local lhs = return_field_data(node.node, mapDef, "lhsIndex", file_content)

---@type string
local rhs = return_field_data(node.node, mapDef, "rhsIndex", file_content)
local buf_local = false
local opts_arg = node.node:child(mapDef.optsIndex)
-- the opts table arg of `vim.keymap.set` is optional, only
-- do this check if it's present.
if opts_arg then
-- check for `buffer = <any>`, since we shouldn't show
-- buf-local mappings
buf_local = vim.treesitter
.get_node_text(opts_arg, file_content)
:gsub("[\n\r]", "")
:match("^.*(buffer%s*=.+)%s*[,}].*$") ~= nil
end

if not buf_local then
table.insert(tsKemaps, {
mode = mode,
lhs = lhs,
rhs = rhs,
from_file = file_path,
})
end
end
end
end

local function_call_query = ts.parse_query(
"lua",
build_function_call_query(keyMapSet)
)

for match in tsQuery.iter_prepared_matches(function_call_query, tree, file_content, 0, -1) do
for expCap, node in pairs(match) do
if expCap == "args" then
local parent = vim.treesitter.get_node_text(node.node:parent():child(0), file_content)
local mapDef = keyMapSet[parent]
---@type string
local mode = return_field_data(node.node, mapDef, "modeIndex", file_content)

---@type string
local lhs = return_field_data(node.node, mapDef, "lhsIndex", file_content)

---@type string
local rhs = return_field_data(node.node, mapDef, "rhsIndex", file_content)
local buf_local = false
local opts_arg = node.node:child(7)
local opts_arg = node.node:child(mapDef.optsIndex)
-- the opts table arg of `vim.keymap.set` is optional, only
-- do this check if it's present.
if opts_arg then
Expand All @@ -62,18 +196,9 @@ local function find_maps_in_file(file_path)

if not buf_local then
table.insert(tsKemaps, {
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 = mode,
lhs = lhs,
rhs = rhs,
from_file = file_path,
})
end
Expand All @@ -88,7 +213,7 @@ end
local function get_keymaps_from_vim()
local vimKeymaps = {}

local vim_keymaps_raw = vim.api.nvim_get_keymap("n")
local vim_keymaps_raw = vim.api.nvim_get_keymap("")
print("Collecting vim keymaps")
for _, vim_keymap in ipairs(vim_keymaps_raw) do
table.insert(vimKeymaps, {
Expand Down Expand Up @@ -132,4 +257,6 @@ function M.get_all_keymaps()
return returnKeymaps
end

M.find_maps_in_file = find_maps_in_file

return M

0 comments on commit 449c1e8

Please sign in to comment.