Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
folke committed Apr 26, 2021
0 parents commit 970e79f
Show file tree
Hide file tree
Showing 8 changed files with 362 additions and 0 deletions.
13 changes: 13 additions & 0 deletions .lua-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# https://github.com/Koihik/LuaFormatter/blob/master/docs/Style-Config.md
column_limit: 100
indent_width: 2
continuation_indent_width: 2
use_tab: false
chop_down_parameter: true
chop_down_table: true
chop_down_kv_table: true
single_quote_to_double_quote: true
spaces_inside_table_braces: true
align_parameter: true
keep_simple_control_block_one_line: true
extra_sep_at_table_end: true
Empty file added README.md
Empty file.
15 changes: 15 additions & 0 deletions lua/which-key/colors.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
local M = {}

local links = {
[""] = "Function",
Seperator = "DiffAdded",
Group = "Keyword",
Desc = "Identifier",
WhichKeyFloating = "NormalFloat",
}

function M.setup()
for k, v in pairs(links) do vim.api.nvim_command("hi def link WhichKey" .. k .. " " .. v) end
end

return M
16 changes: 16 additions & 0 deletions lua/which-key/config.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
local M = {}

M.namespace = vim.api.nvim_create_namespace("WhichKey")

---@class Options
local defaults = {}

---@type Options
M.options = {}

---@return Options
function M.setup(options) M.options = vim.tbl_deep_extend("force", {}, defaults, options or {}) end

M.setup()

return M
11 changes: 11 additions & 0 deletions lua/which-key/init.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
local Keys = require("which-key.keys")
local config = require("which-key.config")
require("which-key.colors").setup()

local M = {}

function M.setup(options) config.setup(options) end

M.register = Keys.register

return M
140 changes: 140 additions & 0 deletions lua/which-key/keys.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
local M = {}

function M.t(str) return vim.api.nvim_replace_termcodes(str, true, true, true) end

function M.parse_keys(keystr)
local keys = {}
local special = nil
for i = 1, #keystr, 1 do
local c = keystr:sub(i, i)
if c == "<" then
special = "<"
elseif c == ">" and special then
table.insert(keys, special .. ">")
special = nil
elseif special then
special = special .. c
else
table.insert(keys, c)
end
end
local ret = { keys = M.t(keystr), term = {}, nvim = {} }
for i, key in pairs(keys) do
if key == " " then key = "<space>" end
if i == 1 and vim.g.mapleader and M.t(key) == M.t(vim.g.mapleader) then key = "<leader>" end
table.insert(ret.term, M.t(key))
table.insert(ret.nvim, key)
end
return ret
end

function M.get_keymap(mode, prefix, buf)
local mappings = {}

local map = function(keymap)
prefix = M.t(prefix)
for _, mapping in pairs(keymap) do
local id = M.t(mapping.lhs)
if id:sub(1, #prefix) == prefix then
mapping.id = id
local idx = M.get_idx(mode, mapping.id)
local buf_idx = M.get_idx(mode, mapping.id, buf)
mapping.keys = M.parse_keys(mapping.lhs)
mapping = vim.tbl_deep_extend("force", {}, mapping, M.mappings[idx] or {},
M.mappings[buf_idx] or {}, mapping)
mappings[id] = mapping
end
end
end

-- global mappings
map(vim.api.nvim_get_keymap(mode))
-- buffer local mappings
if buf then map(vim.api.nvim_buf_get_keymap(buf, mode)) end

return mappings
end

function M.parse_mappings(ret, value, prefix)
prefix = prefix or ""
if type(value) == "string" then
table.insert(ret, { prefix = prefix, label = value })
elseif type(value) == "table" then
if #value == 0 then
-- key group
for k, v in pairs(value) do if k ~= "name" then M.parse_mappings(ret, v, prefix .. k) end end
if prefix ~= "" then
table.insert(ret, { prefix = prefix, label = value.name or "+prefix", group = true })
end
else
-- key mapping
local mapping = { prefix = prefix, opts = {} }
for k, v in pairs(value) do
if k == 1 then
mapping.label = v
elseif k == 2 then
mapping.cmd = mapping.label
mapping.label = v
elseif k == "noremap" then
mapping.opts.noremap = v
elseif k == "silent" then
mapping.opts.silent = v
elseif k == "bufnr" then
mapping.opts.bufnr = v
else
error("Invalid key mapping: " .. vim.inspect(value))
end
end
table.insert(ret, mapping)
end
else
error("Invalid mapping " .. vim.inspect(value))
end
return ret
end

M.mappings = {}

function M.register(mappings, opts)
opts = opts or {}

local prefix = opts.prefix or ""
opts.prefix = nil

local mode = opts.mode or "n"
opts.mode = nil

mappings = M.parse_mappings({}, mappings, prefix)

for _, mapping in pairs(mappings) do
mapping.id = M.t(mapping.prefix)
mapping.opts = vim.tbl_deep_extend("force", { silent = true, noremap = true }, opts,
mapping.opts or {})
local cmd = mapping.cmd
if mapping.group then
mapping.opts.noremap = false
cmd = string.format([[<cmd>lua require("which-key.view").on_keys(%q)<cr>]], mapping.prefix)
end

if cmd then
if mapping.opts.bufnr ~= nil then
local buf = mapping.opts.bufnr
mapping.opts.bufnr = nil
vim.api.nvim_buf_set_keymap(buf, mode, mapping.prefix, cmd, mapping.opts)
mapping.opts.bufnr = buf
else
vim.api.nvim_set_keymap(mode, mapping.prefix, cmd, mapping.opts)
end
end
local idx = M.get_idx(mode, mapping.id, mapping.opts.bufnr)
M.mappings[idx] = vim.tbl_deep_extend("force", M.mappings[idx] or {}, mapping)
end
end

function M.get_idx(mode, keyid, buf)
local ret = mode .. ":" .. keyid
if buf then ret = ":" .. buf end
return ret
end

return M
38 changes: 38 additions & 0 deletions lua/which-key/text.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---@class Text
---@field lines string[]
---@field hl Highlight[]
---@field lineNr number
---@field current string
local Text = {}
Text.__index = Text

function Text:new()
local this = { lines = {}, hl = {}, lineNr = 0, current = "" }
setmetatable(this, self)
return this
end

function Text:nl()
table.insert(self.lines, self.current)
self.current = ""
self.lineNr = self.lineNr + 1
end

function Text:render(str, group, opts)
if type(opts) == "string" then opts = { append = opts } end
opts = opts or {}

if group then
if opts.exact ~= true then group = "WhichKey" .. group end
local from = string.len(self.current)
---@class Highlight
local hl
hl = { line = self.lineNr, from = from, to = from + string.len(str), group = group }
table.insert(self.hl, hl)
end
self.current = self.current .. str
if opts.append then self.current = self.current .. opts.append end
if opts.nl then self:nl() end
end

return Text
129 changes: 129 additions & 0 deletions lua/which-key/view.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
local Keys = require("which-key.keys")
local config = require("which-key.config")
local Text = require("which-key.text")

local highlight = vim.api.nvim_buf_add_highlight

---@class View
local M = {}

M.keys = ""
M.buf = nil
M.win = nil

function M.is_valid()
return M.buf and vim.api.nvim_buf_is_valid(M.buf) and vim.api.nvim_buf_is_loaded(M.buf) and
vim.api.nvim_win_is_valid(M.win)

end

function M.show()
if M.is_valid() then return end
M.buf = vim.api.nvim_create_buf(false, true)
M.win = vim.api.nvim_open_win(M.buf, false, {
relative = "editor",
width = vim.o.columns,
height = 10,
focusable = false,
anchor = "SW",
row = vim.o.lines - 2,
col = 0,
style = "minimal",
})
vim.api.nvim_win_set_option(M.win, "winhighlight", "NormalFloat:WhichKeyFloating")
vim.cmd [[autocmd! WinClosed <buffer> lua require("which-key.view").on_close()]]
end

function M.eat(wait)
while true do
local n = wait and vim.fn.getchar() or vim.fn.getchar(0)
if n == 0 then return end
if n == 27 then -- <esc> key
M.on_close()
return
end
M.keys = M.keys .. (type(n) == "number" and vim.fn.nr2char(n) or n)
if wait then
vim.defer_fn(function() M.on_keys(M.keys) end, 0)
return
end
end
end

function M.on_close()
print(M.keys)
M.hide()
end

function M.hide()
if M.buf and vim.api.nvim_buf_is_valid(M.buf) then
vim.api.nvim_buf_delete(M.buf, { force = true })
M.buf = nil
end
if M.win and vim.api.nvim_win_is_valid(M.win) then
vim.api.nvim_win_close(M.win, { force = true })
M.win = nil
end
end

---@param text Text
function M.render_mapping(text, mapping)
text:render(mapping.lhs, "")
text:render("->", "Seperator")
if mapping.group == true then
text:render(mapping.label or mapping.rhs or "", "Group")
else
text:render(mapping.label or mapping.rhs or "", "Desc")
end
text:nl()
end

function M.on_keys(keys)
M.keys = keys or ""
-- eat queued characters
M.eat(false)

local mappings = Keys.get_keymap(vim.api.nvim_get_mode().mode, M.keys,
vim.api.nvim_get_current_buf())

local text = Text:new()
for _, mapping in pairs(mappings) do
-- Exact match found, trigger keymapping
if mapping.id == Keys.t(M.keys) then
if mapping.group ~= true then
M.hide()
vim.api.nvim_feedkeys(M.keys, "m", true)
return
else -- skip this exact prefix group
end
end
M.render_mapping(text, mapping)
end

if #text.lines == 0 then
-- no mappings found. Feed back the keys
M.hide()
vim.api.nvim_feedkeys(M.keys, "n", true)
return
end

if not M.is_valid() then M.show() end

M.render(text)

-- defer further eating on the main loop
vim.defer_fn(function() M.eat(true) end, 0)
end

---@param text Text
function M.render(text)
vim.api.nvim_buf_set_lines(M.buf, 0, -1, false, text.lines)
if vim.api.nvim_buf_is_valid(M.buf) then
vim.api.nvim_buf_clear_namespace(M.buf, config.namespace, 0, -1)
end
for _, data in ipairs(text.hl) do
highlight(M.buf, config.namespace, data.group, data.line, data.from, data.to)
end
end

return M

0 comments on commit 970e79f

Please sign in to comment.