diff --git a/.gitignore b/.gitignore index c60062f..7951005 100644 --- a/.gitignore +++ b/.gitignore @@ -188,3 +188,192 @@ docs/plans docs/specs docs/devlogs # ──────────────────────────────────────────────────────── + +# id: gitig-boilerplate-92f3a89d2c63f6a381be8bebd1862de5 +# ╭──────────────────────────────────────────────────────────╮ +# │ GitIgnore Boilerplate Template │ +# ╰──────────────────────────────────────────────────────────╯ +# +# ──────────────────── OS-Generated Files ──────────────────── +# automatic backup files created by some editors (e.g., Vim, Emacs) +*~ + +# temporary files created if a process still has a handle to a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# MacOS junk +.DS_Store +Thumbs.db + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# files created when an open file is removed but is still being accessed +.nfs* + +# ─────────────────── Debug/Temporary/Testing ──────────────── +# Matches OLD / .OLD +[Oo][Ll][Dd]/ +.[Oo][Ll][Dd]/ + +# Matches DISABLE / .DISABLE +[Dd][Ii][Ss][Aa][Bb][Ll][Ee]/ +.[Dd][Ii][Ss][Aa][Bb][Ll][Ee]/ + +# Matches DISABLED / .DISABLED +[Dd][Ii][Ss][Aa][Bb][Ll][Ee][Dd]/ +.[Dd][Ii][Ss][Aa][Bb][Ll][Ee][Dd]/ + +# Matches DEBUG / .DEBUG +[Dd][Ee][Bb][Uu][Gg]/ +.[Dd][Ee][Bb][Uu][Gg]/ + +# Matches TMP / .TMP +[Tt][Mm][Pp]/ +.[Tt][Mm][Pp]/ + +# Matches TEMP / .TEMP +[Tt][Ee][Mm][Pp]/ +.[Tt][Ee][Mm][Pp]/ + +# Matches TEMPORARY / .TEMPORARY +[Tt][Ee][Mm][Pp][Oo][Rr][Aa][Rr][Yy]/ +.[Tt][Ee][Mm][Pp][Oo][Rr][Aa][Rr][Yy]/ + +# Matches TESTING / .TESTING +[Tt][Ee][Ss][Tt][Ii][Nn][Gg]/ +.[Tt][Ee][Ss][Tt][Ii][Nn][Gg]/ + +# ─────────────────── AI Sessions and Rules ────────────────── +# Matches CLAUDE.md, .claud*, etc. +[Cc][Ll][Aa][Uu][Dd][Ee].[Mm][Dd] +.[Cc][Ll][Aa][Uu][Dd]* + +# Matches GEMINI.md, .gemin*, etc. +[Gg][Ee][Mm][Ii][Nn][Ii].[Mm][Dd] +.[Gg][Ee][Mm][Ii][Nn]* + +# Matches ANTIGRAVITY.md, .antigrav*, etc. +[Aa][nN][Tt][Ii][Gg][Rr][Aa][Vv][Ii][Tt][Yy].[Mm][Dd] +.[Aa][Nn][Tt][Ii][Gg][Rr][Aa][Vv]* + +# Matches AGENTS.md, .remember, etc. +[Aa][Gg][Ee][Nn][Tt][Ss].[Mm][Dd] +.[Rr][Ee][Mm][Ee][Mm][Bb][Ee][Rr] + +# ──────────────────── Planning Artifacts ─────────────────── +# Catalog files generated by pre-implementation analysis passes +.superpowers +docs/superpowers +docs/specs + +# ────────────────────────────────────────────────────────────── + +# id: gi-patterns-54143085ec229f3cf907a5aa2a8fcade +# Created by https://www.toptal.com/developers/gitignore/api/linux +# Edit at https://www.toptal.com/developers/gitignore?templates=linux + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# End of https://www.toptal.com/developers/gitignore/api/linux + +# id: gitig-boilerplate-e76d1ca7170bfb8385be6293da3f57bc +# ╭──────────────────────────────────────────────────────────╮ +# │ GitIgnore Boilerplate Template │ +# ╰──────────────────────────────────────────────────────────╯ +# +# ──────────────────── OS-Generated Files ──────────────────── +# automatic backup files created by some editors (e.g., Vim, Emacs) +*~ + +# temporary files created if a process still has a handle to a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# MacOS junk +.DS_Store +Thumbs.db + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# files created when an open file is removed but is still being accessed +.nfs* + +# ─────────────────── Debug/Temporary/Testing ──────────────── +# Matches OLD / .OLD +[Oo][Ll][Dd]/ +.[Oo][Ll][Dd]/ + +# Matches DISABLE / .DISABLE +[Dd][Ii][Ss][Aa][Bb][Ll][Ee]/ +.[Dd][Ii][Ss][Aa][Bb][Ll][Ee]/ + +# Matches DISABLED / .DISABLED +[Dd][Ii][Ss][Aa][Bb][Ll][Ee][Dd]/ +.[Dd][Ii][Ss][Aa][Bb][Ll][Ee][Dd]/ + +# Matches DEBUG / .DEBUG +[Dd][Ee][Bb][Uu][Gg]/ +.[Dd][Ee][Bb][Uu][Gg]/ + +# Matches TMP / .TMP +[Tt][Mm][Pp]/ +.[Tt][Mm][Pp]/ + +# Matches TEMP / .TEMP +[Tt][Ee][Mm][Pp]/ +.[Tt][Ee][Mm][Pp]/ + +# Matches TEMPORARY / .TEMPORARY +[Tt][Ee][Mm][Pp][Oo][Rr][Aa][Rr][Yy]/ +.[Tt][Ee][Mm][Pp][Oo][Rr][Aa][Rr][Yy]/ + +# Matches TESTING / .TESTING +[Tt][Ee][Ss][Tt][Ii][Nn][Gg]/ +.[Tt][Ee][Ss][Tt][Ii][Nn][Gg]/ + +# ─────────────────── AI Sessions and Rules ────────────────── +# Matches CLAUDE.md, .claud*, etc. +[Cc][Ll][Aa][Uu][Dd][Ee].[Mm][Dd] +.[Cc][Ll][Aa][Uu][Dd]* + +# Matches GEMINI.md, .gemin*, etc. +[Gg][Ee][Mm][Ii][Nn][Ii].[Mm][Dd] +.[Gg][Ee][Mm][Ii][Nn]* + +# Matches ANTIGRAVITY.md, .antigrav*, etc. +[Aa][nN][Tt][Ii][Gg][Rr][Aa][Vv][Ii][Tt][Yy].[Mm][Dd] +.[Aa][Nn][Tt][Ii][Gg][Rr][Aa][Vv]* + +# Matches AGENTS.md, .agents, .remember, etc. +[Aa][Gg][Ee][Nn][Tt][Ss].[Mm][Dd] +.[Aa][Gg][Ee][Nn][Tt][Ss] +.[Rr][Ee][Mm][Ee][Mm][Bb][Ee][Rr] + +# ──────────────────── Planning Artifacts ─────────────────── +# Catalog files generated by pre-implementation analysis passes +.superpowers +docs/superpowers +docs/specs +docs/devlogs + +# ────────────────────────────────────────────────────────────── diff --git a/README.md b/README.md index c16913a..c05564a 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ The configuration is strictly modular: - **Haunt.nvim**: In-buffer annotation and bookmark manager. Integrates with the Snacks picker for browsing bookmarks and exposes `haunt_all` / `haunt_buffer` prompt contexts to Sidekick AI sessions. - **Zen Mode**: Distraction-free editing. - **Qalc**: Inline calculator via `qalculate`. +- **Obsidian.nvim**: Obsidian vault integration for note-taking and knowledge management, with `notebook` (`~/Documents/Notebook`) and `notes` (`~/Documents/Notes`) workspaces. ### LSP & Completion - **Blink.cmp** + **blink.lib**: High-performance Rust-based completion. Auto-detects binary across install layouts; falls back gracefully if unavailable. Rebuilds automatically on `PackChanged`. @@ -65,6 +66,10 @@ The configuration is strictly modular: ### Terminal - **Kitty Scrollback**: Browse Kitty terminal scrollback buffer inside Neovim. +- **Neovide**: When running under the Neovide GUI, adds terminal-style system-clipboard keys: `Ctrl+Shift+V` paste (all modes; in Normal mode an empty line is replaced and the cursor lands after the paste), `Ctrl+Shift+C` copy and `Ctrl+Shift+X` cut (selection in Visual mode, current line in Normal mode). + +### Tracking +- **Vim-wakatime**: Coding-time tracking. Loaded eagerly so it sees `VimEnter`/`BufEnter`; server and API key come from `~/.wakatime.cfg`. Reports as `neovim-wakatime` so Wakapi attributes time to Neovim instead of "Unknown". ## 🛠️ System Dependencies @@ -78,6 +83,7 @@ To ensure all features (pickers, formatters, and LSPs) work correctly, the follo - `lazygit` (Git TUI) - `gh` (GitHub CLI integration) - `xclip` / `xsel` (X11) or `wl-copy` (Wayland) for clipboard sync. +- `wakatime-cli` (Optional, installed automatically for Wakatime tracking) ### Runtime Environments - `Node.js` & `npm` (Copilot and various LSPs) diff --git a/lua/options.lua b/lua/options.lua index 3319750..dc0a632 100644 --- a/lua/options.lua +++ b/lua/options.lua @@ -16,14 +16,14 @@ vim.g.mapleader = " " vim.g.maplocalleader = "\\" -- Indentation -vim.opt.tabstop = 2 -- Tab width in spaces -vim.opt.shiftwidth = 2 -- Indent width for >> and auto-indent +vim.opt.tabstop = 2 -- Tab width in spaces +vim.opt.shiftwidth = 2 -- Indent width for >> and auto-indent vim.opt.expandtab = true -- Use spaces instead of tabs -- Performance and UI defaults -vim.opt.updatetime = 200 -- Faster completion and CursorHold events -vim.opt.autowrite = true -- Enable auto write -vim.opt.number = true -- Show line numbers +vim.opt.updatetime = 200 -- Faster completion and CursorHold events +vim.opt.autowrite = true -- Enable auto write +vim.opt.number = true -- Show line numbers vim.opt.relativenumber = true -- Relative line numbers -- ╭─────────────────────────────────────────────────────────╮ @@ -36,14 +36,14 @@ vim.opt.undofile = true -- Autowrite/Autosave -- This ensures changes are saved on every buffer change or when leaving insert mode. vim.api.nvim_create_autocmd({ "InsertLeave", "TextChanged" }, { - group = vim.api.nvim_create_augroup("autosave", { clear = true }), - pattern = { "*" }, - callback = function() - local buftype = vim.api.nvim_get_option_value("buftype", { buf = 0 }) - if buftype == "" and vim.bo.modified then - vim.cmd("silent! update") - end - end, + group = vim.api.nvim_create_augroup("autosave", { clear = true }), + pattern = { "*" }, + callback = function() + local buftype = vim.api.nvim_get_option_value("buftype", { buf = 0 }) + if buftype == "" and vim.bo.modified then + vim.cmd("silent! update") + end + end, }) -- Automatically reload files changed outside of Neovim @@ -52,14 +52,14 @@ vim.opt.autoread = true -- Auto-reload Triggers -- Reload when focus is gained or when the cursor is idle vim.api.nvim_create_autocmd({ "FocusGained", "CursorHold" }, { - group = vim.api.nvim_create_augroup("autoread_on_focus", { clear = true }), - pattern = "*", - callback = function() - -- Only if the buffer is not modified, to avoid losing unsaved changes - if vim.bo.modified == false then - vim.cmd("checktime") - end - end, + group = vim.api.nvim_create_augroup("autoread_on_focus", { clear = true }), + pattern = "*", + callback = function() + -- Only if the buffer is not modified, to avoid losing unsaved changes + if vim.bo.modified == false then + vim.cmd("checktime") + end + end, }) -- Format Function @@ -67,26 +67,26 @@ vim.api.nvim_create_autocmd({ "FocusGained", "CursorHold" }, { -- but if a range is provided (e.g., via visual selection), -- it formats only that range. vim.api.nvim_create_user_command("Format", function(args) - local range = nil - if args.count ~= -1 then - local end_line = vim.api.nvim_buf_get_lines(0, args.line2 - 1, args.line2, true)[1] - range = { - start = { args.line1, 0 }, - ["end"] = { args.line2, end_line:len() }, - } - end - require("conform").format({ async = true, lsp_format = "fallback", range = range }) + local range = nil + if args.count ~= -1 then + local end_line = vim.api.nvim_buf_get_lines(0, args.line2 - 1, args.line2, true)[1] + range = { + start = { args.line1, 0 }, + ["end"] = { args.line2, end_line:len() }, + } + end + require("conform").format({ async = true, lsp_format = "fallback", range = range }) end, { range = true }) -- FormatOnSave -- Formats the buffer before saving. -- This is a common practice to ensure code is consistently formatted. vim.api.nvim_create_autocmd("BufWritePre", { - pattern = "*", - callback = function() - -- Call Format function - vim.api.nvim_command("Format") - end, + pattern = "*", + callback = function() + -- Call Format function + vim.api.nvim_command("Format") + end, }) -- Return to last-known cursor position when reopening files @@ -109,38 +109,38 @@ vim.api.nvim_create_autocmd("BufReadPost", { -- This ensures that plugins like Snacks.picker and GrugFar work -- relative to the file or project you are currently editing. vim.api.nvim_create_autocmd("BufEnter", { - group = vim.api.nvim_create_augroup("buffer_auto_cd", { clear = true }), - callback = function() - local path = vim.api.nvim_buf_get_name(0) - if path == "" then - return - end + group = vim.api.nvim_create_augroup("buffer_auto_cd", { clear = true }), + callback = function() + local path = vim.api.nvim_buf_get_name(0) + if path == "" then + return + end - -- Skip special buffers, but allow directory buffers (for explorer support) - if vim.bo.buftype ~= "" and vim.bo.filetype ~= "snacks_explorer_tree" then - return - end + -- Skip special buffers, but allow directory buffers (for explorer support) + if vim.bo.buftype ~= "" and vim.bo.filetype ~= "snacks_explorer_tree" then + return + end - -- Get the directory (handle both files and directory paths) - local dir = vim.fn.isdirectory(path) == 1 and path or vim.fn.fnamemodify(path, ":p:h") + -- Get the directory (handle both files and directory paths) + local dir = vim.fn.isdirectory(path) == 1 and path or vim.fn.fnamemodify(path, ":p:h") - -- Find the project root using common markers, fallback to the directory itself - local root = vim.fs.root(dir, { ".git", "lua", "package.json", "go.mod", "Cargo.toml", "Makefile" }) or dir + -- Find the project root using common markers, fallback to the directory itself + local root = vim.fs.root(dir, { ".git", "lua", "package.json", "go.mod", "Cargo.toml", "Makefile" }) or dir - -- Change directory if it's different and valid - if root and vim.fn.isdirectory(root) == 1 and root ~= vim.fn.getcwd() then - vim.fn.chdir(root) - end - end, + -- Change directory if it's different and valid + if root and vim.fn.isdirectory(root) == 1 and root ~= vim.fn.getcwd() then + vim.fn.chdir(root) + end + end, }) -- Grug-far QoL: Close with 'q' vim.api.nvim_create_autocmd("FileType", { - group = vim.api.nvim_create_augroup("grug_far_q_to_close", { clear = true }), - pattern = "grug-far", - callback = function(args) - vim.keymap.set("n", "q", "close", { buffer = args.buf, silent = true, desc = "Close Grug-far" }) - end, + group = vim.api.nvim_create_augroup("grug_far_q_to_close", { clear = true }), + pattern = "grug-far", + callback = function(args) + vim.keymap.set("n", "q", "close", { buffer = args.buf, silent = true, desc = "Close Grug-far" }) + end, }) -- ╭─────────────────────────────────────────────────────────╮ @@ -157,47 +157,97 @@ local title_group = vim.api.nvim_create_augroup("TerminalTitle", { clear = true -- Helper function to update the terminal title local function set_terminal_title(title) - -- If in Kitty, we use the direct escape sequence as it's reliable - if is_kitty then - io.stdout:write("\27]2;" .. title .. "\7") - -- Fallback: Use Neovim's built-in title management for other terminals - -- (Standard OSC 2 sequences are supported by most, but opt.title is safer) - elseif os.getenv("TERM") ~= "linux" then - vim.opt.title = true - vim.opt.titlestring = title - end + -- If in Kitty, we use the direct escape sequence as it's reliable + if is_kitty then + io.stdout:write("\27]2;" .. title .. "\7") + -- Fallback: Use Neovim's built-in title management for other terminals + -- (Standard OSC 2 sequences are supported by most, but opt.title is safer) + elseif os.getenv("TERM") ~= "linux" then + vim.opt.title = true + vim.opt.titlestring = title + end end -- Terminal title updates on buffer enter and window enter vim.api.nvim_create_autocmd({ "BufEnter", "BufWinEnter" }, { - group = title_group, - callback = function() - local buftype = vim.bo.buftype - local filetype = vim.bo.filetype - local filename = vim.fn.expand("%:t") + group = title_group, + callback = function() + local buftype = vim.bo.buftype + local filetype = vim.bo.filetype + local filename = vim.fn.expand("%:t") - -- 1. Specifically catch Snacks Terminal or standard Terminals - if filetype == "snacks_terminal" or buftype == "terminal" then - set_terminal_title("NVIM: Terminal") + -- 1. Specifically catch Snacks Terminal or standard Terminals + if filetype == "snacks_terminal" or buftype == "terminal" then + set_terminal_title("NVIM: Terminal") - -- 2. Handle normal files (buftype is empty) - elseif buftype == "" and filename ~= "" then - set_terminal_title("NVIM: " .. filename) + -- 2. Handle normal files (buftype is empty) + elseif buftype == "" and filename ~= "" then + set_terminal_title("NVIM: " .. filename) - -- 3. Handle the empty start screen - elseif filename == "" and buftype == "" then - set_terminal_title("Neovim") - end - end, + -- 3. Handle the empty start screen + elseif filename == "" and buftype == "" then + set_terminal_title("Neovim") + end + end, }) -- Reset the title back to the shell name when you quit Neovim vim.api.nvim_create_autocmd("VimLeave", { - group = title_group, - callback = function() - -- On leave, direct stdout is more reliable than setting an option - if is_kitty or os.getenv("TERM") ~= "linux" then - io.stdout:write("\27]2;" .. shell_name .. "\7") - end - end, + group = title_group, + callback = function() + -- On leave, direct stdout is more reliable than setting an option + if is_kitty or os.getenv("TERM") ~= "linux" then + io.stdout:write("\27]2;" .. shell_name .. "\7") + end + end, }) + +-- ╭─────────────────────────────────────────────────────────╮ +-- │ Neovide Configuration │ +-- ╰─────────────────────────────────────────────────────────╯ + +if vim.g.neovide then + -- ========================================== + -- Paste (Ctrl+Shift+V) + -- ========================================== + -- Normal mode: Smart paste system clipboard (replaces empty line and moves cursor to end) + vim.keymap.set('n', '', function() + local cur_line = vim.api.nvim_get_current_line() + + if cur_line == '' then + local lines = vim.fn.getreg('+', 1, true) + if #lines > 0 then + local r = vim.api.nvim_win_get_cursor(0)[1] + vim.api.nvim_buf_set_lines(0, r - 1, r, false, lines) + + -- Calculate position at the end of the newly pasted text + local target_row = r + #lines - 1 + local target_col = math.max(0, #lines[#lines] - 1) + vim.api.nvim_win_set_cursor(0, { target_row, target_col }) + end + else + -- "]+p puts and positions cursor after the pasted text + vim.cmd('normal! "+]p') + end + end, { desc = 'Paste system clipboard' }) + vim.keymap.set('i', '', '+', { desc = 'Paste system clipboard' }) + vim.keymap.set('c', '', '+', { desc = 'Paste system clipboard' }) + vim.keymap.set('v', '', '"+p', { desc = 'Paste system clipboard' }) + vim.keymap.set('t', '', [["+pi]], { desc = 'Paste system clipboard' }) + + -- ========================================== + -- Copy (Ctrl+Shift+C) + -- ========================================== + -- Visual mode: Yank selected text directly to system clipboard + vim.keymap.set('v', '', '"+y', { desc = 'Copy to system clipboard' }) + -- Normal mode: Yank the current line directly to system clipboard + vim.keymap.set('n', '', '"+yy', { desc = 'Copy line to system clipboard' }) + + -- ========================================== + -- Cut (Ctrl+Shift+X) + -- ========================================== + -- Visual mode: Delete selected text and store in system clipboard + vim.keymap.set('v', '', '"+d', { desc = 'Cut to system clipboard' }) + -- Normal mode: Delete the current line and store in system clipboard + vim.keymap.set('n', '', '"+dd', { desc = 'Cut line to system clipboard' }) +end diff --git a/lua/plugins.lua b/lua/plugins.lua index 81dcb61..97b2159 100644 --- a/lua/plugins.lua +++ b/lua/plugins.lua @@ -124,6 +124,19 @@ Config.plugins.persistence = { } require("persistence").setup(Config.plugins.persistence) +-- Vim-wakatime +-- Eagerly loaded: it hooks VimEnter/BufEnter, which a deferred load would miss. +-- Server/api_key come from ~/.wakatime.cfg, shared with other WakaTime clients. +vim.pack.add({ { src = "https://github.com/wakatime/vim-wakatime", name = "vim-wakatime" } }) + +-- Wakatime Config +-- Wakapi derives the editor from a `-wakatime/` user-agent token; the +-- default `wakatime.nvim` has none, so time lands under "Unknown". +Config.plugins.wakatime = { + plugin_name = "neovim-wakatime", +} +require("wakatime").setup(Config.plugins.wakatime) + -- Lazy-loaded Plugins local lazyload = require("lazyload") @@ -135,7 +148,7 @@ lazyload.on_vim_enter(function() signcolumn = true, current_line_blame = true, -- Required for the statusline variable to update current_line_blame_opts = { - virt_text = false, -- Disable virtual text as we'll use the statusline + virt_text = false, -- Disable virtual text as we'll use the statusline }, }) @@ -547,11 +560,11 @@ lazyload.on_vim_enter(function() }, -- you can enable a preset for easier configuration presets = { - bottom_search = true, -- use a classic bottom cmdline for search - command_palette = true, -- position the cmdline and popupmenu together + bottom_search = true, -- use a classic bottom cmdline for search + command_palette = true, -- position the cmdline and popupmenu together long_message_to_split = true, -- long messages will be sent to a split - inc_rename = true, -- enables an input dialog for inc-rename.nvim - lsp_doc_border = false, -- add a border to hover docs and signature help + inc_rename = true, -- enables an input dialog for inc-rename.nvim + lsp_doc_border = false, -- add a border to hover docs and signature help }, routes = { { @@ -596,4 +609,25 @@ lazyload.on_vim_enter(function() }, } require("sidekick").setup(Config.plugins.sidekick) + + vim.pack.add { + { + src = "https://github.com/obsidian-nvim/obsidian.nvim", + version = vim.version.range "*", -- use latest release, remove to use latest commit + }, + } + + require("obsidian").setup { + legacy_commands = false, -- this will be removed in 4.0.0 + workspaces = { + { + name = "notebook", + path = "~/Documents/Notebook", + }, + { + name = "notes", + path = "~/Documents/Notes", + }, + }, + } end) diff --git a/nvim-pack-lock.json b/nvim-pack-lock.json index daa254a..fa507c2 100644 --- a/nvim-pack-lock.json +++ b/nvim-pack-lock.json @@ -124,6 +124,11 @@ "rev": "4fc505ac7bd7692824a142e96e5f529c133862f8", "src": "https://github.com/nvim-tree/nvim-web-devicons" }, + "obsidian.nvim": { + "rev": "7a2b7caf41de196de66f27c1969d0d429810621a", + "src": "https://github.com/obsidian-nvim/obsidian.nvim", + "version": ">=0.0.0" + }, "persistence": { "rev": "b20b2a7887bd39c1a356980b45e03250f3dce49c", "src": "https://github.com/folke/persistence.nvim" @@ -144,6 +149,10 @@ "rev": "6fa6b57cda8459e1e4b2ca34df702f55242f4e4d", "src": "https://github.com/mbbill/undotree" }, + "vim-wakatime": { + "rev": "9f8a1d3b9c6f3a948988a0896b3227c1e1f74a58", + "src": "https://github.com/wakatime/vim-wakatime" + }, "which-key": { "rev": "3aab2147e74890957785941f0c1ad87d0a44c15a", "src": "https://github.com/folke/which-key.nvim"