ThePrimeagen / harpoon

MIT License
7.08k stars 377 forks source link

Documentation on how to swap/create terminals #487

Open daviareias opened 9 months ago

daviareias commented 9 months ago

I've recently transitioned from Harpoon1 to Harpoon2 and have encountered some challenges in adapting to the new setup, particularly concerning terminal management.

In Harpoon1, configuring and managing terminals felt more straightforward, but in the current version, I find myself missing clear examples or documentation to achieve similar functionality.

To address this gap, I have attempted to implement my own solution for managing terminal buffers. Below is the script I'm currently using:

local harpoon = require("harpoon")

harpoon:setup({
       -- settings here
    terms = { },
})

local function get_terminal_buffer_ids()
    local terminal_buffer_ids = {}
    local buffers = vim.api.nvim_list_bufs()
    for _, buf in ipairs(buffers) do
        if vim.bo[buf].buftype == "terminal" then
            table.insert(terminal_buffer_ids, buf)
        end
    end
    return terminal_buffer_ids
end

---@param index number
local function select_term(index)
    local terminal_ids = get_terminal_buffer_ids()
    local n_of_terms = #terminal_ids

    if index > n_of_terms then
        create_terminal()
        print("Creating terminal", index)
        -- just append the newly open terminal
        harpoon:list(“terms”):append()
    else
        -- find in list
        print("selecting terminal", index)
        harpoon:list(“terms”):select(index)
    end
end

vim.keymap.set("n", "<leader>`", function()
    select_term(1)
end)

vim.keymap.set("n", "<leader><Tab>", function()
    select_term(2)
end)
ThePrimeagen commented 9 months ago

yes, i have an open ticket #350 which already has the ground work for it to be done (select_with_nil option)

i just need the config at this point added. If you are confident that this is a good script i will forward it into the project under the place i want it

daviareias commented 9 months ago

yes, i have an open ticket #350 which already has the ground work for it to be done (select_with_nil option)

i just need the config at this point added. If you are confident that this is a good script i will forward it into the project under the place i want it

It seems to be working 90% of the time, it just happens that sometimes I get this error that will go away if I reopen neovim or delete the open terminals:

E5108: Error executing lua: ...im/site/pack/packer/start/harpoon/lua/harpoon/config.lua:128: Cursor position outside buffer
stack traceback:
        [C]: in function 'nvim_win_set_cursor'
        ...im/site/pack/packer/start/harpoon/lua/harpoon/config.lua:128: in function 'select'
        ...nvim/site/pack/packer/start/harpoon/lua/harpoon/list.lua:182: in function 'select'
        ...e/nvim/site/pack/packer/start/harpoon/lua/harpoon/ui.lua:177: in function 'select_menu_item'
        ...im/site/pack/packer/start/harpoon/lua/harpoon/buffer.lua:21: in function 'run_select_command'

This error will keep creating new terminals with my current script, so it's still not good enough to be adopted.

I think I saw this error in another ticker, I'll tell you If I find the reason behind it.

daviareias commented 9 months ago

Ok I think I came with a better way, the error is caused by trying to select a deleted buffer (closed terminal in this case).

I've came with an autocmd to automatically delete exited terminals from the list, I've been using it for the last month and seems to be working consistently.

I would make a pull request, but I'm not sure how to deal with the terminal when it gets closed by telescope.

harpoon.setup({
    -- Your global settings here
    terms = {
        settings = {
            save_on_toggle = false,
            select_with_nil = false,
            sync_on_ui_close = false,
        },
    },
})

---@type HarpoonList
local term_list = harpoon.list("terms")

---@return string name of the created terminal
local function create_terminal()
    vim.cmd("terminal")
    local buf_id = vim.api.nvim_get_current_buf()
    return vim.api.nvim_buf_get_name(buf_id)
end

---@param index number: The index of the terminal to select.
local function select_term(index)
    if index > term_list:length() then
        create_terminal()
        print("Creating terminal", index)
        -- just append the newly open terminal
        term_list:append()
    else
        -- find in list
        print("selecting terminal", index)
        term_list:select(index)
    end
end

-- TODO this wont work when term gets deleted by telescope
local function remove_closed_terms()
    for _, term in ipairs(term_list.items) do
        local bufnr = vim.fn.bufnr(term.value)
        if bufnr == -1 then
            print("Removing:" .. term.value)
            term_list:remove(term)
        end
        -- can get id here with nvim_buf_get_name because buffer is already deleted
        --term_list:remove(term_name)
        --
    end
end

-- Autocommand to remove closed terminal from the list

-- "VimEnter" cleans terminals that were saved when you closed vim for the last time but were not removed
vim.api.nvim_create_autocmd({ "TermClose", "VimEnter" }, {
    pattern = "*",
    callback = remove_closed_terms,
})

-- This is needed because closing term with bd! won't trigger "TermClose"
vim.api.nvim_create_autocmd({ "BufDelete", "BufUnload" }, {
    pattern = "term://*",
    callback = remove_closed_terms,
})

vim.keymap.set("n", "<leader>`", function()
    select_term(1)
end)

vim.keymap.set("n", "<leader><Tab>", function()
    select_term(2)
end)

-- Command that I use for debugging
vim.api.nvim_create_user_command("HarpoonShowTermList", function()
    harpoon.ui:toggle_quick_menu(term_list)
end, {})
jeroenvermunt commented 6 months ago

Thanks Daviareias, I added this to my setup and it seems to be working for now.

The sendCommand function would be nice as well, something like this:

local function send_command(index, command)
  select_term(index)
  local channel_id = vim.b.terminal_job_id
  vim.api.nvim_chan_send(channel_id, command)
end
n8tlarsen commented 2 months ago

Just added this to my personal config. For today's latest commit 0378a6c, the following changes are required:

---@type HarpoonList
local term_list = harpoon:list("terms") -- note the : instead of .

---@param index number: The index of the terminal to select.
local function select_term(index)
    if index > term_list:length() then
        create_terminal()
        print("Creating terminal", index)
        -- just append the newly open terminal
        term_list:add() -- using add() as append() is depricated
    else
        -- find in list
        print("selecting terminal", index)
        term_list:select(index)
    end
end