harpoon icon indicating copy to clipboard operation
harpoon copied to clipboard

wip: add ui_context

Open maxrzaw opened this issue 1 year ago • 2 comments

I've been playing around trying to make a custom list that will show the files relative to my current context. I would like to accomplish this by passing in context to the UI when I call my keymap. This works well because I can pass in the context I need to evaluate how I want to display it when I call my keymap.

vim.keymap.set("n", "<leader>h", function()
    local harpoon = require("harpoon")
    local path = vim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf())
    -- ^ this wont work when called in HarpoonList:display() because it is the
    -- harpoon buffer which is in a different directory than the file I am editing
    harpoon.ui:toggle_quick_menu(harpoon:list("relative"), {
        context = path
    })
end)

This is working well for my use case, but is a WIP for a couple reasons:

  1. I haven't done any unit tests
  2. I think the display methods in HarpoonList need to be refactored into the ui so we have access to the context without having to pass it into HarpoonList.
  3. Not sure about other use cases

This is related to issue #466

maxrzaw avatar Jan 01 '24 19:01 maxrzaw

Here is my config that is going with it. I haven't completely ironed out the display logic for when to show ../dir/file vs /complete/path/to/file, but this is how I am using it.

-- Function to find the root directory of the project
local function find_project_root()
    ---@type string
    local current_dir = vim.uv.cwd()
    local marker_files = { ".git", "package.json", ".sln" }

    -- Check each parent directory for the existence of a marker file or directory
    while current_dir ~= "/" do
        for _, marker in ipairs(marker_files) do
            local marker_path = current_dir .. "/" .. marker
            if vim.fn.isdirectory(marker_path) == 1 or vim.fn.filereadable(marker_path) == 1 then
                return current_dir
            end
        end
        current_dir = vim.fn.resolve(current_dir .. "/..")
    end
    -- If no marker file or directory is found, return the original directory
    return vim.uv.cwd()
end

return {
    "ThePrimeagen/harpoon",
    branch = "harpoon2",
    dev = true,
    lazy = false,
    dependencies = { "nvim-lua/plenary.nvim" },
    config = function()
        local Path = require("plenary.path")
        local Harpoon = require("harpoon")
        local default_config = Harpoon.config.default

        Harpoon:setup({
            settings = {
                save_on_toggle = true,
                sync_on_ui_close = true,
                key = function()
                    return find_project_root()
                end,
            },
            relative = {
                select = function(list_item, list, options)
                    default_config.select(list_item, list, options)
                end,
                get_root_dir = function()
                    return find_project_root()
                end,
                equals = function(a, b)
                    return a.value == b.value
                end,
                create_list_item = function(config, name)
                    local path = vim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf())
                    name = name or path
                    if string.sub(name, 1, 1) ~= "/" then
                        local dir = Path:new(path):parent().filename
                        path = dir .. "/" .. name
                    end
                    local bufnr = vim.fn.bufnr(path, false)

                    local pos = { 1, 0 }
                    if bufnr ~= -1 then
                        pos = vim.api.nvim_win_get_cursor(0)
                    end

                    return {
                        value = path,
                        context = {
                            row = pos[1],
                            col = pos[2],
                        },
                    }
                end,
                display = function(ui_context, list_item)
                    local dir = Path:new(ui_context):parent().filename
                    return Path:new(list_item.value):make_relative(dir)
                end,
                BufLeave = function(arg, list)
                    local bufnr = arg.buf
                    local bufname = vim.api.nvim_buf_get_name(bufnr)
                    local item = nil
                    for _, it in ipairs(list.items) do
                        local value = it.value
                        if value == bufname then
                            item = it
                        end
                    end

                    if item then
                        local pos = vim.api.nvim_win_get_cursor(0)

                        item.context.row = pos[1]
                        item.context.col = pos[2]
                    end
                end,
            },
        })

        -- Harpoon
        vim.keymap.set("n", "<leader>m", function()
            Harpoon:list("relative"):append()
        end)
        vim.keymap.set("n", "<leader>h", function()
            local path = vim.api.nvim_buf_get_name(vim.api.nvim_get_current_buf())
            Harpoon.ui:toggle_quick_menu(Harpoon:list("relative"), {
                border = "rounded",
                title_pos = "center",
                context = path,
            })
        end)
    end,
}

maxrzaw avatar Jan 01 '24 20:01 maxrzaw

The UI_CREATE event provides a current_file context field (introduced in #457). You can see it in action here. Perhaps it can be of service?

pockata avatar Jan 11 '24 18:01 pockata