NStefan002 / screenkey.nvim

Screencast your keys in Neovim
MIT License
291 stars 5 forks source link
keys lua neovim neovim-plugin screencast screenkey

Screenkey.nvim

Screenkey is a Neovim plugin that displays the keys you are typing in a floating window, just like screenkey does. It is useful for screencasts, presentations, and live coding sessions.

📺 Showcase

https://github.com/NStefan002/screenkey.nvim/assets/100767853/29ea0949-4fd3-4d00-b5a3-2c249bb84360

⚡️ Requirements

📋 Installation

lazy:

return {
    "NStefan002/screenkey.nvim",
    lazy = false,
    version = "*", -- or branch = "dev", to use the latest commit
}

packer:

use({ "NStefan002/screenkey.nvim", tag = "*" })

rocks.nvim

:Rocks install screenkey.nvim

[!NOTE]

  • There is no need to call the setup function, only call it if you need to change some options
  • There is no need to lazy load Screenkey, it lazy loads by default.

⚙️ Configuration

require("screenkey").setup({
    win_opts = {
        row = vim.o.lines - vim.o.cmdheight - 1,
        col = vim.o.columns - 1,
        relative = "editor",
        anchor = "SE",
        width = 40,
        height = 3,
        border = "single",
        title = "Screenkey",
        title_pos = "center",
        style = "minimal",
        focusable = false,
        noautocmd = true,
    },
    compress_after = 3,
    clear_after = 3,
    disable = {
        filetypes = {},
        buftypes = {},
        events = false,
    },
    show_leader = true,
    group_mappings = false,
    display_infront = {},
    display_behind = {},
    filter = function(keys)
        return keys
    end,
    keys = {
        ["<TAB>"] = "󰌒",
        ["<CR>"] = "󰌑",
        ["<ESC>"] = "Esc",
        ["<SPACE>"] = "␣",
        ["<BS>"] = "󰌥",
        ["<DEL>"] = "Del",
        ["<LEFT>"] = "",
        ["<RIGHT>"] = "",
        ["<UP>"] = "",
        ["<DOWN>"] = "",
        ["<HOME>"] = "Home",
        ["<END>"] = "End",
        ["<PAGEUP>"] = "PgUp",
        ["<PAGEDOWN>"] = "PgDn",
        ["<INSERT>"] = "Ins",
        ["<F1>"] = "󱊫",
        ["<F2>"] = "󱊬",
        ["<F3>"] = "󱊭",
        ["<F4>"] = "󱊮",
        ["<F5>"] = "󱊯",
        ["<F6>"] = "󱊰",
        ["<F7>"] = "󱊱",
        ["<F8>"] = "󱊲",
        ["<F9>"] = "󱊳",
        ["<F10>"] = "󱊴",
        ["<F11>"] = "󱊵",
        ["<F12>"] = "󱊶",
        ["CTRL"] = "Ctrl",
        ["ALT"] = "Alt",
        ["SUPER"] = "󰘳",
        ["<leader>"] = "<leader>",
    },
})
option explanation
win_opts see :h nvim_open_win, note1: other options from nvim_open_win help can be provided (such as win, bufpos, zindex etc.), the ones listed above are just defaults)
compress after compress input when repeated times (for example jjjj will be compressed to j..x4)
clear_after clear the input after <clear_after> seconds of inactivity
disable temporarily disable screenkey (for example when inside of the terminal)
disable.filetypes for example: toggleterm or toml
disable.events disable User events
disable.buftypes see :h 'buftype', for example: terminal
group_mappings for example: <leader>sf opens up a fuzzy finder, if the group_mappings option is set to true, every time you open up a fuzzy finder with <leader>sf, Screenkey will show ␣sf instead of ␣ s f to indicate that the used key combination was a defined mapping.
show_leader if this option is set to true, in the last example instead of ␣ s f Screenkey will display <leader> s f (of course, if the <space> is <leader>), if the current key is not a defined mapping, Screenkey will display <space> as
display_infront[^1] if the floating window containing the buffer of the same filetype as in display_infront is opened, screenkey window will be reopened in front of that window (if necessary), Note: you can define filetypes as lua regex, for example "Telescope*" to match every filetype that starts with Telescope
display_behind[^2] if the floating window containing the buffer of the same filetype as in display_behind is opened, screenkey window will be reopened behind of that window (if necessary), Note: you can define filetypes as lua regex, for example "Telescope*" to match every filetype that starts with Telescope
filter function that takes an array of key-is_mapping (string-boolean) pairs (keys) as its argument and returns a filtered array of same pairs, allowing customization of which keys should be displayed, Note: this feature is experimental and subject to change (see #38 for the progress of this feature and see below for the example of how to use it)
keys how to display the special keys

[^1]: This is currently an experimental feature. Please report any issues you encounter. Use it responsibly, do not set too many conditions, as it can slow down the plugin. Also, if the conflict occurs (e.g. two floating windows are present at the same time - one with the filetype that matches the display_infront condition and the other with the filetype that matches the display_behind condition), nothing will happen (this is subject to change)

[^2]: Same as above

❓ How to use

vim.g.screenkey_statusline_component = true

vim.keymap.set("n", "<leader>ssc", function()
    vim.g.screenkey_statusline_component = not vim.g.screenkey_statusline_component
end, { desc = "Toggle screenkey statusline component" })

require("lualine").setup({
    -- other options ...
    sections = {
        -- other sections ...
        lualine_c = {
            -- other components ...
            function()
                return require("screenkey").get_keys()
            end,
        },
    },
})
require("screenkey").redraw()

or

:Screenkey redraw

local screenkey = require("screenkey")
local notify = require("notify")
local toggleScreenKey = function()
    vim.cmd("Screenkey toggle")
    -- change notification position
    notify.setup({
        top_down = screenkey.is_active(),
    })
end

vim.keymap.set("n", "<leader>tsk", toggleScreenKey, { desc = "[T]oggle [S]creen[K]ey" })

[!NOTE] If you're using a terminal inside of the Neovim, and you want screenkey to automatically stop displaying your keys when you're > inside of the terminal, see disable option in the plugin configuration.

require("heirline").setup({
    statusline = {
        {
            provider = function()
                return require("screenkey").get_keys()
            end,
            update = {
                "User",
                pattern = "Screenkey*",
                callback = vim.schedule_wrap(function()
                    vim.cmd("redrawstatus")
                end),
            },
        },
    },
})
-- filter out j and k keys, but only if they are not part of a mapping
require("screenkey").setup({
    filter = function(keys)
        return vim.iter(keys)
            :filter(function(k)
                return (k.key ~= "j" and k.key ~= "k") or k.is_mapping
            end)
            :totable()
    end,
})

🙏 I took inspiration (and some code) from

👀 Similar projects