Skip to content

xavierchen0/hooks.nvim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Hooks 🪝

A very small, dead-simple Harpoon-like tool with minimal configuration but with all the functionality you need!

  • Inspired from ThePrimeagen's Harpoon2
  • ~ small: only 147 lines of Lua code (not counting comments and blank lines)
image

Table of Contents

  1. Motivation
  2. Why use something like Harpoon?
  3. Features and Usage
  4. Installation
  5. Full Config
  6. APIs

Motivation

You might be wondering: "If Harpoon2 is already great, why use Hooks?"

Hooks isn't meant to compete with feature-rich plugins; it's meant to be simple and disappear into your config. I built this for myself who want to reduce my plugin footprint and want to know exactly how my tool works.

Some other reasons:

  • No complex caching logic or heavy dependencies. Just 147 lines of transparent Lua.

  • Only a single init.lua file and you can read the entire source code in a single sitting.

  • Leverages Neovim's built-in floating windows, namespaces, and autocommands rather than custom UI layers.

Note: If you are looking for something more featureful or customisable, I recommend Harpoon2. This plugin's only value is that it is dead simple such that you can easily read and understand the source code, and figure out what this tool is doing. You can even copy the source code into your config, and truly make this functionality yours (and I really encourage it!).

Why use something like Harpoon?

ThePrimeagen has made a video summarising why, but I will briefly explain here.

During development, you will often only be interacting with a handful of files at any point in time.

For e.g., you are working on File A, but often jump to File B as there is where you store your helper functions, but you will also jump to File C as there is where you write your configuration files or environment variables. But at any point in time, you will only be interacting with roughly less than 5 files.

What you want is a keymap that you can just press and then jump to that file immediately, without opening a file picker or manually entering the filepath. However, the keymap must be flexible and easily changed, you can't be always jumping to your config to set keymaps to manually define specific filepaths. That is too tedious and timeconsuming considering you might be constantly changing your current set of working files!

Some pros and cons of native alternatives:

  1. Global Marks :h mark-motions:
    • Pros:
      • Persistence across separate sessions
    • Cons:
      • Always jumping to specific location where you have marked it. But often times, when you jump back to the file, you want the cursor to be at where u last left the file.
      • As a result, you constantly need to remark the file which is troublesome, and runs the risk of u re-marking the wrong mark (e.g. mark to B instead of A (intended))
  2. Arglist :h arglist:
    • Pros:
      • Remembers where you left the file with a BufReadPost autocmd.
    • Cons:
      • No persistence across sessions. If you exit neovim, all your previously set arglist disappears.
      • Difficult to manipulate the arglist and once manipulated, it can be difficult to jump accurately to the right file (i.e. [count]argument).

Features and Usage

  1. Easily add current file to a keymap.
  2. Easily press your defined keymap to jump to a file.
  3. Easily add/remove/edit keymaps and filepaths through an editable buffer, similar to oil.nvim.
  4. Git-local keymaps and files, or fallback to a global one if not in a git directory (i.e. Specific tagged files and keymaps for each git repo).

For my own config, I have defined a prefix as <leader>m.

Adding File to Keymap To add a current file to a keymap, I defined it as <leader>ma[some-character], in which I add the a (mnemonic for add) to the prefix.

For example, to add the current file to a keymap that I will assign to 1, I will do <leader>ma1. If I want to map another file to 2, I will :e anotherfile.txt, and then do <leader>ma2 to add that file to the keymap.

Jumping to a File Using the above-defined keymaps, if I want to jump to the first file, I will just press <leader>m1, and to jump to the second file, I will just press <leader>m2.

Opening the Editable Menu To open the editable menu, I will press <leader>mm.

When editing the menu, you must ensure you follow this syntax [key] = valid-filepath in order for the tagging to succeed. Otherwise, Hooks will throw an error and not save your changes. Lines with issues will be shown with an extmark X.

It is also important to note that when entering valid-filepath, the file must exists!

Menu

image

Menu with issues

image

Note: These are my keybinds, and you are encouraged to set your own.

Installation

lazy.nvim
{
  'xavierchen0/hooks.nvim',
  config = function()
    local h = require("hooks")
    -- Add your basic setup here (see below for full config)
  end
}
Neovim native package
git clone --depth=1 https://github.com/xavierchen0/hooks.nvim.git \
  "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/pack/hooks.nvim/start/hooks.nvim

or

vim.pack.add({
  { src = "https://github.com/xavierchen0/hooks.nvim" },
})

Full Config

local h = require("hooks")
-- Typically, you want to keep this to a small number as you are usually
-- interacting with a small set of working files
local valid_keys = {
  "0", "1", "2", "3", "4", "5",
}
-- or if you want to use all keys
-- local valid_keys = {
--   "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
--   "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
--   "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
--   "u", "v", "w", "x", "y", "z",
-- }

local prefix = "<leader>m"

for _, key in ipairs(valid_keys) do
  -- Add the keymaps to jump to a file
  vim.keymap.set("n", prefix .. key, function()
    h.jump(key)
  end, { desc = "Hooks: Jump to [" .. key .. "]" })

  -- Add the keymaps to add a file
  vim.keymap.set("n", prefix .. "a" .. key, function()
    h.add(key)
  end, { desc = "Hooks: Add current file to [" .. key .. "]" })
end

-- Opens the editable menu
vim.keymap.set("n", prefix .. "m", function()
  h.menu()
end, { desc = "Hooks: Open editable menu" })

-- Optional: Autocmd to return to last position in a file
-- Useful when you just launch nvim, and you used your defined keymaps to jump
-- to a file. Otherwise, when you first jump, your cursor will be at the top
-- of the file.
-- vim.api.nvim_create_autocmd("BufReadPost", {
--   desc = "Go to last edit location when opening a new buffer",
--   pattern = "*",
--   callback = function(event)
--     local exclude = { "gitcommit" }
--     local buf = event.buf
--
--     -- Check if filetype is excluded or if we've already jumped
--     if vim.tbl_contains(exclude, vim.bo[buf].filetype) or vim.b[buf].last_loc_flag then
--       return
--     end
--
--     -- Mark this buffer as processed so we don't jump every time we re-enter it
--     vim.b[buf].last_loc_flag = true
--
--     local mark = vim.api.nvim_buf_get_mark(buf, '"')
--     local lcount = vim.api.nvim_buf_line_count(buf)
--
--     -- If the mark is valid (between line 1 and the end of the file)
--     if mark[1] > 0 and mark[1] <= lcount then
--       pcall(vim.api.nvim_win_set_cursor, 0, mark)
--     end
--   end,
-- })

-- Optional: use " " (space) as leader key
-- vim.g.mapleader = " "

APIs

Hooks exposes a raw Lua table. There is no setup() function required; simply require the module and call the functions.

require("hooks").add(key)

Assigns the current buffer's file path to the specified slot.

  • key (string): The slot identifier (e.g., "a", "1").

require("hooks").jump(key)

Navigates to the file stored in the specified slot.

  • key (string): The slot identifier.

require("hooks").menu()

Opens the Floating Management Window to view, edit, or delete slots.

  • No parameters

About

Neovim plugin: A Dead Simple Harpoon

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages